Merge branch 'ecc-devel-mpg' into development
diff --git a/include/polarssl/config.h b/include/polarssl/config.h
index b69bd8c..7640a96 100644
--- a/include/polarssl/config.h
+++ b/include/polarssl/config.h
@@ -537,6 +537,43 @@
 #define POLARSSL_DHM_C
 
 /**
+ * \def POLARSSL_ECDH_C
+ *
+ * Enable the elliptic curve Diffie-Hellman library.
+ *
+ * Module:  library/ecdh.c
+ * Caller:
+ *
+ * Requires: POLARSSL_ECP_C
+ */
+#define POLARSSL_ECDH_C
+
+/**
+ * \def POLARSSL_ECDSA_C
+ *
+ * Enable the elliptic curve DSA library.
+ *
+ * Module:  library/ecdsa.c
+ * Caller:
+ *
+ * Requires: POLARSSL_ECP_C
+ */
+#define POLARSSL_ECDSA_C
+
+/**
+ * \def POLARSSL_ECP_C
+ *
+ * Enable the elliptic curve over GF(p) library.
+ *
+ * Module:  library/ecp.c
+ * Caller:  library/ecdh.c
+ *          library/ecdsa.c
+ *
+ * Requires: POLARSSL_BIGNUM_C
+ */
+#define POLARSSL_ECP_C
+
+/**
  * \def POLARSSL_ENTROPY_C
  *
  * Enable the platform-specific entropy code.
@@ -887,6 +924,7 @@
  * Caller:
  */
 #define POLARSSL_XTEA_C
+
 /* \} name */
 
 #endif /* config.h */
diff --git a/include/polarssl/ecdh.h b/include/polarssl/ecdh.h
new file mode 100644
index 0000000..7f6f4cb
--- /dev/null
+++ b/include/polarssl/ecdh.h
@@ -0,0 +1,176 @@
+/**
+ * \file ecdh.h
+ *
+ * \brief Elliptic curve Diffie-Hellman
+ *
+ *  Copyright (C) 2006-2013, Brainspark B.V.
+ *
+ *  This file is part of PolarSSL (http://www.polarssl.org)
+ *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
+ *
+ *  All rights reserved.
+ *
+ *  This program 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.
+ *
+ *  This program 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.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef POLARSSL_ECDH_H
+#define POLARSSL_ECDH_H
+
+#include "polarssl/ecp.h"
+
+/**
+ * \brief           ECDH context structure
+ */
+typedef struct
+{
+    ecp_group grp;      /*!<  ellipitic curve used      */
+    mpi d;              /*!<  our secret value          */
+    ecp_point Q;        /*!<  our public value          */
+    ecp_point Qp;       /*!<  peer's public value       */
+    mpi z;              /*!<  shared secret             */
+    int point_format;   /*!<  format for point export   */
+}
+ecdh_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief           Generate a public key
+ *
+ * \param grp       ECP group
+ * \param d         Destination MPI (secret exponent)
+ * \param Q         Destination point (public key)
+ * \param f_rng     RNG function
+ * \param p_rng     RNG parameter
+ *
+ * \return          0 if successful,
+ *                  or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code
+ */
+int ecdh_gen_public( const ecp_group *grp, mpi *d, ecp_point *Q,
+                     int (*f_rng)(void *, unsigned char *, size_t),
+                     void *p_rng );
+
+/**
+ * \brief           Compute shared secret
+ *
+ * \param grp       ECP group
+ * \param z         Destination MPI (shared secret)
+ * \param Q         Public key from other party
+ * \param d         Our secret exponent
+ *
+ * \return          0 if successful,
+ *                  or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code
+ */
+int ecdh_compute_shared( const ecp_group *grp, mpi *z,
+                         const ecp_point *Q, const mpi *d );
+
+/**
+ * \brief           Initialize context
+ *
+ * \param ctx       Context to initialize
+ */
+void ecdh_init( ecdh_context *ctx );
+
+/**
+ * \brief           Free context
+ *
+ * \param ctx       Context to free
+ */
+void ecdh_free( ecdh_context *ctx );
+
+/**
+ * \brief           Setup and write the ServerKeyExhange parameters
+ *
+ * \param ctx       ECDH context
+ * \param buf       destination buffer
+ * \param olen      number of chars written
+ * \param f_rng     RNG function
+ * \param p_rng     RNG parameter
+ *
+ * \note            This function assumes that ctx->grp has already been
+ *                  properly set (for example using ecp_use_known_dp).
+ *
+ * \return          0 if successful, or an POLARSSL_ERR_ECP_XXX error code
+ */
+int ecdh_make_params( ecdh_context *ctx, size_t *olen,
+                      unsigned char *buf, size_t blen,
+                      int (*f_rng)(void *, unsigned char *, size_t),
+                      void *p_rng );
+
+/**
+ * \brief           Parse the ServerKeyExhange parameters
+ *
+ * \param ctx       ECDH context
+ * \param buf       $(start of input buffer)
+ * \param end       one past end of buffer
+ *
+ * \return          0 if successful, or an POLARSSL_ERR_ECP_XXX error code
+ */
+int ecdh_read_params( ecdh_context *ctx,
+                      const unsigned char **buf, const unsigned char *end );
+
+/**
+ * \brief           Setup and export the client's public value
+ *
+ * \param ctx       ECDH context
+ * \param olen      number of bytes actually written
+ * \param buf       destination buffer
+ * \param blen      size of destination buffer
+ *
+ * \return          0 if successful, or an POLARSSL_ERR_ECP_XXX error code
+ */
+int ecdh_make_public( ecdh_context *ctx, size_t *olen,
+                      unsigned char *buf, size_t blen,
+                      int (*f_rng)(void *, unsigned char *, size_t),
+                      void *p_rng );
+
+/**
+ * \brief           Parse and import the client's public value
+ *
+ * \param ctx       ECDH context
+ * \param buf       start of input buffer
+ * \param blen      length of input buffer
+ *
+ * \return          0 if successful, or an POLARSSL_ERR_ECP_XXX error code
+ */
+int ecdh_read_public( ecdh_context *ctx,
+                      const unsigned char *buf, size_t blen );
+
+/**
+ * \brief           Derive and export the shared secret
+ *
+ * \param ctx       ECDH context
+ * \param olen      number of bytes written
+ * \param buf       destination buffer
+ * \param blen      buffer length
+ *
+ * \return          0 if successful, or an POLARSSL_ERR_ECP_XXX error code
+ */
+int ecdh_calc_secret( ecdh_context *ctx, size_t *olen,
+                      unsigned char *buf, size_t blen );
+
+/**
+ * \brief          Checkup routine
+ *
+ * \return         0 if successful, or 1 if the test failed
+ */
+int ecdh_self_test( int verbose );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/polarssl/ecdsa.h b/include/polarssl/ecdsa.h
new file mode 100644
index 0000000..2ad986d
--- /dev/null
+++ b/include/polarssl/ecdsa.h
@@ -0,0 +1,84 @@
+/**
+ * \file ecdsa.h
+ *
+ * \brief Elliptic curve DSA
+ *
+ *  Copyright (C) 2006-2013, Brainspark B.V.
+ *
+ *  This file is part of PolarSSL (http://www.polarssl.org)
+ *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
+ *
+ *  All rights reserved.
+ *
+ *  This program 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.
+ *
+ *  This program 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.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef POLARSSL_ECDSA_H
+#define POLARSSL_ECDSA_H
+
+#include "polarssl/ecp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief           Compute ECDSA signature of a previously hashed message
+ *
+ * \param grp       ECP group
+ * \param r         First output integer
+ * \param s         Second output integer
+ * \param d         Private signing key
+ * \param buf       Message hash
+ * \param blen      Length of buf
+ * \param f_rng     RNG function
+ * \param p_rng     RNG parameter
+ *
+ * \return          0 if successful,
+ *                  or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code
+ */
+int ecdsa_sign( const ecp_group *grp, mpi *r, mpi *s,
+                const mpi *d, const unsigned char *buf, size_t blen,
+                int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
+
+/**
+ * \brief           Verify ECDSA signature of a previously hashed message
+ *
+ * \param grp       ECP group
+ * \param buf       Message hash
+ * \param blen      Length of buf
+ * \param Q         Public key to use for verification
+ * \param r         First integer of the signature
+ * \param s         Second integer of the signature
+ *
+ * \return          0 if successful,
+ *                  POLARSSL_ERR_ECP_BAD_INPUT_DATA if signature is invalid
+ *                  or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code
+ */
+int ecdsa_verify( const ecp_group *grp,
+                  const unsigned char *buf, size_t blen,
+                  const ecp_point *Q, const mpi *r, const mpi *s);
+
+/**
+ * \brief          Checkup routine
+ *
+ * \return         0 if successful, or 1 if the test failed
+ */
+int ecdsa_self_test( int verbose );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/polarssl/ecp.h b/include/polarssl/ecp.h
new file mode 100644
index 0000000..8debda4
--- /dev/null
+++ b/include/polarssl/ecp.h
@@ -0,0 +1,428 @@
+/**
+ * \file ecp.h
+ *
+ * \brief Elliptic curves over GF(p)
+ *
+ *  Copyright (C) 2006-2013, Brainspark B.V.
+ *
+ *  This file is part of PolarSSL (http://www.polarssl.org)
+ *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
+ *
+ *  All rights reserved.
+ *
+ *  This program 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.
+ *
+ *  This program 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.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef POLARSSL_ECP_H
+#define POLARSSL_ECP_H
+
+#include "polarssl/bignum.h"
+
+/*
+ * ECP error codes
+ */
+#define POLARSSL_ERR_ECP_BAD_INPUT_DATA                    -0x4F80  /**< Bad input parameters to function. */
+#define POLARSSL_ERR_ECP_BUFFER_TOO_SMALL                  -0x4F80  /**< The buffer is too small to write to. */
+#define POLARSSL_ERR_ECP_GENERIC                           -0x4F00  /**<  Generic ECP error */
+
+/**
+ * \brief           ECP point structure (jacobian coordinates)
+ *
+ * \note            All functions expect and return points satisfying
+ *                  the following condition: Z == 0 or Z == 1. (Other
+ *                  values of Z are used by internal functions only.)
+ *                  The point is zero, or "at infinity", if Z == 0.
+ *                  Otherwise, X and Y are its standard (affine) coordinates.
+ */
+typedef struct
+{
+    mpi X;          /*!<  the point's X coordinate  */
+    mpi Y;          /*!<  the point's Y coordinate  */
+    mpi Z;          /*!<  the point's Z coordinate  */
+}
+ecp_point;
+
+/*
+ * RFC 4492 defines an enum NamedCurve with two-bytes values
+ */
+typedef uint16_t ecp_group_id;
+
+/**
+ * \brief           ECP group structure
+ *
+ * The curves we consider are defined by y^2 = x^3 - 3x + B mod P,
+ * and a generator for a large subgroup of order N is fixed.
+ *
+ * pbits and nbits must be the size of P and N in bits.
+ *
+ * If modp is NULL, reduction modulo P is done using a generic
+ * algorithm. Otherwise, it must point to a function that takes an mpi
+ * in the range 0..2^(2*pbits) and transforms it in-place in an integer
+ * of little more than pbits, so that the integer may be efficiently
+ * brought in the 0..P range by a few additions or substractions. It
+ * must return 0 on success and a POLARSSL_ERR_ECP_XXX error on failure.
+ */
+typedef struct
+{
+    ecp_group_id id;    /*!<  RFC 4492 group ID                 */
+    mpi P;              /*!<  prime modulus of the base field   */
+    mpi B;              /*!<  constant term in the equation     */
+    ecp_point G;        /*!<  generator of the subgroup used    */
+    mpi N;              /*!<  the order of G                    */
+    size_t pbits;       /*!<  number of bits in P               */
+    size_t nbits;       /*!<  number of bits in N               */
+    int (*modp)(mpi *); /*!<  function for fast reduction mod P */
+}
+ecp_group;
+
+/**
+ * RFC 5114 defines a number of standardized ECP groups for use with TLS.
+ *
+ * These also are the NIST-recommended ECP groups, are the random ECP groups
+ * recommended by SECG, and include the two groups used by NSA Suite B.
+ * There are known as secpLLLr1 with LLL = 192, 224, 256, 384, 521.
+ *
+ * \warning This library does not support validation of arbitrary domain
+ * parameters. Therefore, only well-known domain parameters from trusted
+ * sources should be used. See ecp_use_known_dp().
+ *
+ * \note The values are taken from RFC 4492's enum NamedCurve.
+ */
+#define POLARSSL_ECP_DP_SECP192R1   19
+#define POLARSSL_ECP_DP_SECP224R1   21
+#define POLARSSL_ECP_DP_SECP256R1   23
+#define POLARSSL_ECP_DP_SECP384R1   24
+#define POLARSSL_ECP_DP_SECP521R1   25
+
+/**
+ * Maximum bit size of the groups (that is, of N)
+ */
+#define POLARSSL_ECP_MAX_N_BITS     521
+
+/*
+ * Maximum window size (actually, NAF width) used for point multipliation.
+ * Default: 7.
+ * Minimum value: 2. Maximum value: 8.
+ *
+ * Result is an array of at most ( 1 << ( POLARSSL_ECP_WINDOW_SIZE - 1 ) )
+ * points used for point multiplication, so at most 64 by default.
+ * In practice, most curves will use less precomputed points.
+ *
+ * Reduction in size may reduce speed for big curves.
+ */
+#define POLARSSL_ECP_WINDOW_SIZE    7   /**< Maximum NAF width used. */
+
+/*
+ * Point formats, from RFC 4492's enum ECPointFormat
+ */
+#define POLARSSL_ECP_PF_UNCOMPRESSED    0   /**< Uncompressed point format */
+#define POLARSSL_ECP_PF_COMPRESSED      1   /**< Compressed point format */
+
+/*
+ * Some other constants from RFC 4492
+ */
+#define POLARSSL_ECP_TLS_NAMED_CURVE    3   /**< ECCurveType's named_curve */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief           Initialize a point (as zero)
+ */
+void ecp_point_init( ecp_point *pt );
+
+/**
+ * \brief           Initialize a group (to something meaningless)
+ */
+void ecp_group_init( ecp_group *grp );
+
+/**
+ * \brief           Free the components of a point
+ */
+void ecp_point_free( ecp_point *pt );
+
+/**
+ * \brief           Free the components of an ECP group
+ */
+void ecp_group_free( ecp_group *grp );
+
+/**
+ * \brief           Set a point to zero
+ *
+ * \param pt        Destination point
+ *
+ * \return          0 if successful,
+ *                  POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed
+ */
+int ecp_set_zero( ecp_point *pt );
+
+/**
+ * \brief           Tell if a point is zero
+ *
+ * \param pt        Point to test
+ *
+ * \return          1 if point is zero, 0 otherwise
+ */
+int ecp_is_zero( ecp_point *pt );
+
+/**
+ * \brief           Copy the contents of point Q into P
+ *
+ * \param P         Destination point
+ * \param Q         Source point
+ *
+ * \return          0 if successful,
+ *                  POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed
+ */
+int ecp_copy( ecp_point *P, const ecp_point *Q );
+
+/**
+ * \brief           Check that a point is a valid public key on this curve
+ *
+ * \param grp       Curve/group the point should belong to
+ * \param pt        Point to check
+ *
+ * \return          0 if point is a valid public key,
+ *                  POLARSSL_ERR_ECP_GENERIC otherwise.
+ *
+ * \note            This function only checks the point is non-zero, has valid
+ *                  coordinates and lies on the curve, but not that it is
+ *                  indeed a multiple of G. This is additional check is more
+ *                  expensive, isn't required by standards, and shouldn't be
+ *                  necessary if the group used has a small cofactor. In
+ *                  particular, it is useless for the NIST groups which all
+ *                  have a cofactor of 1.
+ */
+int ecp_check_pubkey( const ecp_group *grp, const ecp_point *pt );
+
+/**
+ * \brief           Import a non-zero point from two ASCII strings
+ *
+ * \param P         Destination point
+ * \param radix     Input numeric base
+ * \param x         First affine coordinate as a null-terminated string
+ * \param y         Second affine coordinate as a null-terminated string
+ *
+ * \return          0 if successful, or a POLARSSL_ERR_MPI_XXX error code
+ */
+int ecp_point_read_string( ecp_point *P, int radix,
+                           const char *x, const char *y );
+
+/**
+ * \brief           Import an ECP group from null-terminated ASCII strings
+ *
+ * \param grp       Destination group
+ * \param radix     Input numeric base
+ * \param p         Prime modulus of the base field
+ * \param b         Constant term in the equation
+ * \param gx        The generator's X coordinate
+ * \param gy        The generator's Y coordinate
+ * \param n         The generator's order
+ *
+ * \return          0 if successful, or a POLARSSL_ERR_MPI_XXX error code
+ *
+ * \note            Sets all fields except modp.
+ */
+int ecp_group_read_string( ecp_group *grp, int radix,
+                           const char *p, const char *b,
+                           const char *gx, const char *gy, const char *n);
+
+/**
+ * \brief           Export a point into unsigned binary data
+ *
+ * \param grp       Group to which the point should belong
+ * \param P         Point to export
+ * \param format    Point format, should be a POLARSSL_ECP_PF_XXX macro
+ * \param olen      Length of the actual output
+ * \param buf       Output buffer
+ * \param buflen    Length of the output buffer
+ *
+ * \return          0 if successful,
+ *                  or POLARSSL_ERR_ECP_BAD_INPUT_DATA
+ *                  or POLARSSL_ERR_ECP_BUFFER_TOO_SMALL
+ */
+int ecp_point_write_binary( const ecp_group *grp, const ecp_point *P,
+                            int format, size_t *olen,
+                            unsigned char *buf, size_t buflen );
+
+/**
+ * \brief           Import a point from unsigned binary data
+ *
+ * \param grp       Group to which the point should belong
+ * \param P         Point to import
+ * \param buf       Input buffer
+ * \param ilen      Actual length of input
+ *
+ * \return          0 if successful,
+ *                  POLARSSL_ERR_ECP_GENERIC if input is invalid
+ *                  POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed
+ *
+ * \note            This function does NOT check that the point actually
+ *                  belongs to the given group, see ecp_check_pubkey() for
+ *                  that.
+ */
+int ecp_point_read_binary( const ecp_group *grp, ecp_point *P,
+                           const unsigned char *buf, size_t ilen );
+
+/**
+ * \brief           Set a group using well-known domain parameters
+ *
+ * \param grp       Destination group
+ * \param index     Index in the list of well-known domain parameters
+ *
+ * \return          O if successful,
+ *                  POLARSSL_ERR_MPI_XXX if initialization failed
+ *                  POLARSSL_ERR_ECP_GENERIC if index is out of range
+ *
+ * \note            Index should be a value of RFC 4492's enum NamdeCurve,
+ *                  possibly in the form of a POLARSSL_ECP_DP_XXX macro.
+ */
+int ecp_use_known_dp( ecp_group *grp, ecp_group_id id );
+
+/**
+ * \brief           Set a group from a TLS ECParameters record
+ *
+ * \param grp       Destination group
+ * \param buf       &(Start of input buffer)
+ * \param len       Buffer length
+ *
+ * \return          O if successful,
+ *                  POLARSSL_ERR_MPI_XXX if initialization failed
+ *                  POLARSSL_ERR_ECP_BAD_INPUT_DATA if input is invalid
+ */
+int ecp_tls_read_group( ecp_group *grp, const unsigned char **buf, size_t len );
+
+/**
+ * \brief           Write the TLS ECParameters record for a group
+ *
+ * \param grp       ECP group used
+ * \param olen      Number of bytes actually written
+ * \param buf       Buffer to write to
+ * \param blen      Buffer length
+ *
+ * \return          0 if successful,
+ *                  or POLARSSL_ERR_ECP_BUFFER_TOO_SMALL
+ */
+int ecp_tls_write_group( const ecp_group *grp, size_t *olen,
+                         unsigned char *buf, size_t blen );
+
+/**
+ * \brief           Import a point from a TLS ECPoint record
+ *
+ * \param grp       ECP group used
+ * \param pt        Destination point
+ * \param buf       $(Start of input buffer)
+ * \param len       Buffer length
+ *
+ * \return          O if successful,
+ *                  POLARSSL_ERR_MPI_XXX if initialization failed
+ *                  POLARSSL_ERR_ECP_BAD_INPUT_DATA if input is invalid
+ */
+int ecp_tls_read_point( const ecp_group *grp, ecp_point *pt,
+                        const unsigned char **buf, size_t len );
+
+/**
+ * \brief           Export a point as a TLS ECPoint record
+ *
+ * \param grp       ECP group used
+ * \param pt        Point to export
+ * \param format    Export format
+ * \param buf       Buffer to write to
+ * \param len       Buffer length
+ *
+ * \return          0 if successful,
+ *                  or POLARSSL_ERR_ECP_BAD_INPUT_DATA
+ *                  or POLARSSL_ERR_ECP_BUFFER_TOO_SMALL
+ */
+int ecp_tls_write_point( const ecp_group *grp, const ecp_point *pt,
+                         int format, size_t *olen,
+                         unsigned char *buf, size_t blen );
+
+/**
+ * \brief           Addition: R = P + Q
+ *
+ * \param grp       ECP group
+ * \param R         Destination point
+ * \param P         Left-hand point
+ * \param Q         Right-hand point
+ *
+ * \return          0 if successful,
+ *                  POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed
+ */
+int ecp_add( const ecp_group *grp, ecp_point *R,
+             const ecp_point *P, const ecp_point *Q );
+
+/**
+ * \brief           Subtraction: R = P - Q
+ *
+ * \param grp       ECP group
+ * \param R         Destination point
+ * \param P         Left-hand point
+ * \param Q         Right-hand point
+ *
+ * \return          0 if successful,
+ *                  POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed
+ */
+int ecp_sub( const ecp_group *grp, ecp_point *R,
+             const ecp_point *P, const ecp_point *Q );
+
+/**
+ * \brief           Multiplication by an integer: R = m * P
+ *
+ * \param grp       ECP group
+ * \param R         Destination point
+ * \param m         Integer by which to multiply
+ * \param P         Point to multiply
+ *
+ * \return          0 if successful,
+ *                  POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed
+ *                  POLARSSL_ERR_ECP_GENERIC if m < 0 of m has greater bit
+ *                  length than N, the number of points in the group.
+ *
+ * \note            This function executes a constant number of operations
+ *                  for random m in the allowed range.
+ */
+int ecp_mul( const ecp_group *grp, ecp_point *R,
+             const mpi *m, const ecp_point *P );
+
+/**
+ * \brief           Generate a keypair
+ *
+ * \param grp       ECP group
+ * \param d         Destination MPI (secret part)
+ * \param Q         Destination point (public part)
+ * \param f_rng     RNG function
+ * \param p_rng     RNG parameter
+ *
+ * \return          0 if successful,
+ *                  or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code
+ */
+int ecp_gen_keypair( const ecp_group *grp, mpi *d, ecp_point *Q,
+                     int (*f_rng)(void *, unsigned char *, size_t),
+                     void *p_rng );
+
+/**
+ * \brief          Checkup routine
+ *
+ * \return         0 if successful, or 1 if the test failed
+ */
+int ecp_self_test( int verbose );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/polarssl/error.h b/include/polarssl/error.h
index 96815a7..32dc0a7 100644
--- a/include/polarssl/error.h
+++ b/include/polarssl/error.h
@@ -3,7 +3,7 @@
  *
  * \brief Error to string translation
  *
- *  Copyright (C) 2006-2010, Brainspark B.V.
+ *  Copyright (C) 2006-2013, Brainspark B.V.
  *
  *  This file is part of PolarSSL (http://www.polarssl.org)
  *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
@@ -68,6 +68,7 @@
  * SHA2      1  0x0078-0x0078
  * SHA4      1  0x007A-0x007A
  * PBKDF2    1  0x007C-0x007C
+ * ECP       1  0x007E-0x007E
  *
  * High-level module nr (3 bits - 0x1...-0x8...)
  * Name     ID  Nr of Errors
@@ -75,6 +76,7 @@
  * X509     2   21
  * DHM      3   6
  * RSA      4   9
+ * ECP      4   1 (Started from top)
  * MD       5   4
  * CIPHER   6   5
  * SSL      6   2 (Started from top)
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index 1ddd44e..c96a42a 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -16,6 +16,9 @@
      debug.c
      des.c
      dhm.c
+     ecp.c
+     ecdh.c
+     ecdsa.c
      entropy.c
      entropy_poll.c
      error.c
diff --git a/library/Makefile b/library/Makefile
index 2fcbce0..1d3a38a 100644
--- a/library/Makefile
+++ b/library/Makefile
@@ -39,7 +39,9 @@
 		blowfish.o	camellia.o					\
 		certs.o		cipher.o	cipher_wrap.o	\
 		ctr_drbg.o	debug.o		des.o			\
-		dhm.o		entropy.o	entropy_poll.o	\
+		dhm.o		ecdh.o		ecdsa.o			\
+		ecp.o									\
+		entropy.o	entropy_poll.o				\
 		error.o		gcm.o		havege.o		\
 		md.o		md_wrap.o	md2.o			\
 		md4.o		md5.o		net.o			\
diff --git a/library/ecdh.c b/library/ecdh.c
new file mode 100644
index 0000000..e30ae3a
--- /dev/null
+++ b/library/ecdh.c
@@ -0,0 +1,232 @@
+/*
+ *  Elliptic curve Diffie-Hellman
+ *
+ *  Copyright (C) 2006-2013, Brainspark B.V.
+ *
+ *  This file is part of PolarSSL (http://www.polarssl.org)
+ *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
+ *
+ *  All rights reserved.
+ *
+ *  This program 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.
+ *
+ *  This program 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.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * References:
+ *
+ * SEC1 http://www.secg.org/index.php?action=secg,docs_secg
+ * RFC 4492
+ */
+
+#include "polarssl/config.h"
+
+#if defined(POLARSSL_ECDH_C)
+
+#include "polarssl/ecdh.h"
+
+/*
+ * Generate public key: simple wrapper around ecp_gen_keypair
+ */
+int ecdh_gen_public( const ecp_group *grp, mpi *d, ecp_point *Q,
+                     int (*f_rng)(void *, unsigned char *, size_t),
+                     void *p_rng )
+{
+    return ecp_gen_keypair( grp, d, Q, f_rng, p_rng );
+}
+
+/*
+ * Compute shared secret (SEC1 3.3.1)
+ */
+int ecdh_compute_shared( const ecp_group *grp, mpi *z,
+                         const ecp_point *Q, const mpi *d )
+{
+    int ret;
+    ecp_point P;
+
+    ecp_point_init( &P );
+
+    /*
+     * Make sure Q is a valid pubkey before using it
+     */
+    MPI_CHK( ecp_check_pubkey( grp, Q ) );
+
+    MPI_CHK( ecp_mul( grp, &P, d, Q ) );
+
+    if( ecp_is_zero( &P ) )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    MPI_CHK( mpi_copy( z, &P.X ) );
+
+cleanup:
+    ecp_point_free( &P );
+
+    return( ret );
+}
+
+/*
+ * Initialize context
+ */
+void ecdh_init( ecdh_context *ctx )
+{
+    ecp_group_init( &ctx->grp );
+    mpi_init      ( &ctx->d   );
+    ecp_point_init( &ctx->Q   );
+    ecp_point_init( &ctx->Qp  );
+    mpi_init      ( &ctx->z   );
+    ctx->point_format = POLARSSL_ECP_PF_UNCOMPRESSED;
+}
+
+/*
+ * Free context
+ */
+void ecdh_free( ecdh_context *ctx )
+{
+    if( ctx == NULL )
+        return;
+
+    ecp_group_free( &ctx->grp );
+    mpi_free      ( &ctx->d   );
+    ecp_point_free( &ctx->Q   );
+    ecp_point_free( &ctx->Qp  );
+    mpi_free      ( &ctx->z   );
+}
+
+/*
+ * Setup and write the ServerKeyExhange parameters (RFC 4492)
+ *      struct {
+ *          ECParameters    curve_params;
+ *          ECPoint         public;
+ *      } ServerECDHParams;
+ */
+int ecdh_make_params( ecdh_context *ctx, size_t *olen,
+                      unsigned char *buf, size_t blen,
+                      int (*f_rng)(void *, unsigned char *, size_t),
+                      void *p_rng )
+{
+    int ret;
+    size_t grp_len, pt_len;
+
+    if( ctx == NULL || ctx->grp.pbits == 0 )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    if( ( ret = ecdh_gen_public( &ctx->grp, &ctx->d, &ctx->Q, f_rng, p_rng ) )
+                != 0 )
+        return( ret );
+
+    if( ( ret = ecp_tls_write_group( &ctx->grp, &grp_len, buf, blen ) )
+                != 0 )
+        return( ret );
+
+    buf += grp_len;
+    blen -= grp_len;
+
+    if( ( ret = ecp_tls_write_point( &ctx->grp, &ctx->Q, ctx->point_format,
+                                     &pt_len, buf, blen ) ) != 0 )
+        return( ret );
+
+    *olen = grp_len + pt_len;
+    return 0;
+}
+
+/*
+ * Read the ServerKeyExhange parameters (RFC 4492)
+ *      struct {
+ *          ECParameters    curve_params;
+ *          ECPoint         public;
+ *      } ServerECDHParams;
+ */
+int ecdh_read_params( ecdh_context *ctx,
+                      const unsigned char **buf, const unsigned char *end )
+{
+    int ret;
+
+    ecdh_init( ctx );
+
+    if( ( ret = ecp_tls_read_group( &ctx->grp, buf, end - *buf ) ) != 0 )
+        return( ret );
+
+    if( ( ret = ecp_tls_read_point( &ctx->grp, &ctx->Qp, buf, end - *buf ) )
+                != 0 )
+        return( ret );
+
+    return 0;
+}
+
+/*
+ * Setup and export the client public value
+ */
+int ecdh_make_public( ecdh_context *ctx, size_t *olen,
+                      unsigned char *buf, size_t blen,
+                      int (*f_rng)(void *, unsigned char *, size_t),
+                      void *p_rng )
+{
+    int ret;
+
+    if( ctx == NULL || ctx->grp.pbits == 0 )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    if( ( ret = ecdh_gen_public( &ctx->grp, &ctx->d, &ctx->Q, f_rng, p_rng ) )
+                != 0 )
+        return( ret );
+
+    return ecp_tls_write_point( &ctx->grp, &ctx->Q, ctx->point_format,
+                                olen, buf, blen );
+}
+
+/*
+ * Parse and import the client's public value
+ */
+int ecdh_read_public( ecdh_context *ctx,
+                      const unsigned char *buf, size_t blen )
+{
+    if( ctx == NULL )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    return ecp_tls_read_point( &ctx->grp, &ctx->Qp, &buf, blen );
+}
+
+/*
+ * Derive and export the shared secret
+ */
+int ecdh_calc_secret( ecdh_context *ctx, size_t *olen,
+                      unsigned char *buf, size_t blen )
+{
+    int ret;
+
+    if( ctx == NULL )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    if( ( ret = ecdh_compute_shared( &ctx->grp, &ctx->z, &ctx->Qp, &ctx->d ) )
+                != 0 )
+        return( ret );
+
+    *olen = mpi_size( &ctx->z );
+    return mpi_write_binary( &ctx->z, buf, blen );
+}
+
+
+#if defined(POLARSSL_SELF_TEST)
+
+/*
+ * Checkup routine
+ */
+int ecdh_self_test( int verbose )
+{
+    return( verbose++ );
+}
+
+#endif
+
+#endif /* defined(POLARSSL_ECDH_C) */
diff --git a/library/ecdsa.c b/library/ecdsa.c
new file mode 100644
index 0000000..d683457
--- /dev/null
+++ b/library/ecdsa.c
@@ -0,0 +1,190 @@
+/*
+ *  Elliptic curve DSA
+ *
+ *  Copyright (C) 2006-2013, Brainspark B.V.
+ *
+ *  This file is part of PolarSSL (http://www.polarssl.org)
+ *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
+ *
+ *  All rights reserved.
+ *
+ *  This program 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.
+ *
+ *  This program 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.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * References:
+ *
+ * SEC1 http://www.secg.org/index.php?action=secg,docs_secg
+ */
+
+#include "polarssl/config.h"
+
+#if defined(POLARSSL_ECDSA_C)
+
+#include "polarssl/ecdsa.h"
+
+/*
+ * Derive a suitable integer for group grp from a buffer of length len
+ * SEC1 4.1.3 step 5 aka SEC1 4.1.4 step 3
+ */
+static int derive_mpi( const ecp_group *grp, mpi *x,
+                       const unsigned char *buf, size_t blen )
+{
+    size_t n_size = (grp->nbits + 7) / 8;
+    return( mpi_read_binary( x, buf, blen > n_size ? n_size : blen ) );
+}
+
+/*
+ * Compute ECDSA signature of a hashed message (SEC1 4.1.3)
+ * Obviously, compared to SEC1 4.1.3, we skip step 4 (hash message)
+ */
+int ecdsa_sign( const ecp_group *grp, mpi *r, mpi *s,
+                const mpi *d, const unsigned char *buf, size_t blen,
+                int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
+{
+    int ret, key_tries, sign_tries;
+    ecp_point R;
+    mpi k, e;
+
+    ecp_point_init( &R );
+    mpi_init( &k );
+    mpi_init( &e );
+
+    sign_tries = 0;
+    do
+    {
+        /*
+         * Steps 1-3: generate a suitable ephemeral keypair
+         */
+        key_tries = 0;
+        do
+        {
+            MPI_CHK( ecp_gen_keypair( grp, &k, &R, f_rng, p_rng ) );
+            MPI_CHK( mpi_copy( r, &R.X ) );
+
+            if( key_tries++ > 10 )
+                return( POLARSSL_ERR_ECP_GENERIC );
+        }
+        while( mpi_cmp_int( r, 0 ) == 0 );
+
+        /*
+         * Step 5: derive MPI from hashed message
+         */
+        MPI_CHK( derive_mpi( grp, &e, buf, blen ) );
+
+        /*
+         * Step 6: compute s = (e + r * d) / k mod n
+         */
+        MPI_CHK( mpi_mul_mpi( s, r, d ) );
+        MPI_CHK( mpi_add_mpi( &e, &e, s ) );
+        MPI_CHK( mpi_inv_mod( s, &k, &grp->N ) );
+        MPI_CHK( mpi_mul_mpi( s, s, &e ) );
+        MPI_CHK( mpi_mod_mpi( s, s, &grp->N ) );
+
+        if( sign_tries++ > 10 )
+            return( POLARSSL_ERR_ECP_GENERIC );
+    }
+    while( mpi_cmp_int( s, 0 ) == 0 );
+
+cleanup:
+    ecp_point_free( &R );
+    mpi_free( &k );
+    mpi_free( &e );
+
+    return( ret );
+}
+
+/*
+ * Verify ECDSA signature of hashed message (SEC1 4.1.4)
+ * Obviously, compared to SEC1 4.1.3, we skip step 2 (hash message)
+ */
+int ecdsa_verify( const ecp_group *grp,
+                  const unsigned char *buf, size_t blen,
+                  const ecp_point *Q, const mpi *r, const mpi *s)
+{
+    int ret;
+    mpi e, s_inv, u1, u2;
+    ecp_point R, P;
+
+    ecp_point_init( &R ); ecp_point_init( &P );
+    mpi_init( &e ); mpi_init( &s_inv ); mpi_init( &u1 ); mpi_init( &u2 );
+
+    /*
+     * Step 1: make sure r and s are in range 1..n-1
+     */
+    if( mpi_cmp_int( r, 1 ) < 0 || mpi_cmp_mpi( r, &grp->N ) >= 0 ||
+        mpi_cmp_int( s, 1 ) < 0 || mpi_cmp_mpi( s, &grp->N ) >= 0 )
+    {
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+    }
+
+    /*
+     * Additional precaution: make sure Q is valid
+     */
+    MPI_CHK( ecp_check_pubkey( grp, Q ) );
+
+    /*
+     * Step 3: derive MPI from hashed message
+     */
+    MPI_CHK( derive_mpi( grp, &e, buf, blen ) );
+
+    /*
+     * Step 4: u1 = e / s mod n, u2 = r / s mod n
+     */
+    MPI_CHK( mpi_inv_mod( &s_inv, s, &grp->N ) );
+
+    MPI_CHK( mpi_mul_mpi( &u1, &e, &s_inv ) );
+    MPI_CHK( mpi_mod_mpi( &u1, &u1, &grp->N ) );
+
+    MPI_CHK( mpi_mul_mpi( &u2, r, &s_inv ) );
+    MPI_CHK( mpi_mod_mpi( &u2, &u2, &grp->N ) );
+
+    /*
+     * Step 5: R = u1 G + u2 Q
+     */
+    MPI_CHK( ecp_mul( grp, &R, &u1, &grp->G ) );
+    MPI_CHK( ecp_mul( grp, &P, &u2, Q ) );
+    MPI_CHK( ecp_add( grp, &R, &R, &P ) );
+
+    if( ecp_is_zero( &R ) )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    /*
+     * Step 6: check that xR == r
+     */
+    if( mpi_cmp_mpi( &R.X, r ) != 0 )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+cleanup:
+    ecp_point_free( &R ); ecp_point_free( &P );
+    mpi_free( &e ); mpi_free( &s_inv ); mpi_free( &u1 ); mpi_free( &u2 );
+
+    return( ret );
+}
+
+
+#if defined(POLARSSL_SELF_TEST)
+
+/*
+ * Checkup routine
+ */
+int ecdsa_self_test( int verbose )
+{
+    return( verbose++ );
+}
+
+#endif
+
+#endif /* defined(POLARSSL_ECDSA_C) */
diff --git a/library/ecp.c b/library/ecp.c
new file mode 100644
index 0000000..a76ce70
--- /dev/null
+++ b/library/ecp.c
@@ -0,0 +1,1364 @@
+/*
+ *  Elliptic curves over GF(p)
+ *
+ *  Copyright (C) 2006-2013, Brainspark B.V.
+ *
+ *  This file is part of PolarSSL (http://www.polarssl.org)
+ *  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
+ *
+ *  All rights reserved.
+ *
+ *  This program 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.
+ *
+ *  This program 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.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * References:
+ *
+ * SEC1 http://www.secg.org/index.php?action=secg,docs_secg
+ * GECC = Guide to Elliptic Curve Cryptography - Hankerson, Menezes, Vanstone
+ * FIPS 186-3 http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
+ * RFC 4492 for the related TLS structures and constants
+ */
+
+#include "polarssl/config.h"
+
+#if defined(POLARSSL_ECP_C)
+
+#include "polarssl/ecp.h"
+#include <limits.h>
+#include <stdlib.h>
+
+#if defined(POLARSSL_SELF_TEST)
+/*
+ * Counts of point addition and doubling operations.
+ * Used to test resistance of point multiplication to SPA/timing attacks.
+ */
+unsigned long add_count, dbl_count;
+#endif
+
+/*
+ * Initialize (the components of) a point
+ */
+void ecp_point_init( ecp_point *pt )
+{
+    if( pt == NULL )
+        return;
+
+    mpi_init( &pt->X );
+    mpi_init( &pt->Y );
+    mpi_init( &pt->Z );
+}
+
+/*
+ * Initialize (the components of) a group
+ */
+void ecp_group_init( ecp_group *grp )
+{
+    if( grp == NULL )
+        return;
+
+    grp->id = 0;
+
+    mpi_init( &grp->P );
+    mpi_init( &grp->B );
+    ecp_point_init( &grp->G );
+    mpi_init( &grp->N );
+
+    grp->pbits = 0;
+    grp->nbits = 0;
+
+    grp->modp = NULL;
+}
+
+/*
+ * Unallocate (the components of) a point
+ */
+void ecp_point_free( ecp_point *pt )
+{
+    if( pt == NULL )
+        return;
+
+    mpi_free( &( pt->X ) );
+    mpi_free( &( pt->Y ) );
+    mpi_free( &( pt->Z ) );
+}
+
+/*
+ * Unallocate (the components of) a group
+ */
+void ecp_group_free( ecp_group *grp )
+{
+    if( grp == NULL )
+        return;
+
+    mpi_free( &grp->P );
+    mpi_free( &grp->B );
+    ecp_point_free( &grp->G );
+    mpi_free( &grp->N );
+}
+
+/*
+ * Set point to zero
+ */
+int ecp_set_zero( ecp_point *pt )
+{
+    int ret;
+
+    MPI_CHK( mpi_lset( &pt->X , 1 ) );
+    MPI_CHK( mpi_lset( &pt->Y , 1 ) );
+    MPI_CHK( mpi_lset( &pt->Z , 0 ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Tell if a point is zero
+ */
+int ecp_is_zero( ecp_point *pt )
+{
+    return( mpi_cmp_int( &pt->Z, 0 ) == 0 );
+}
+
+/*
+ * Copy the contents of Q into P
+ */
+int ecp_copy( ecp_point *P, const ecp_point *Q )
+{
+    int ret;
+
+    MPI_CHK( mpi_copy( &P->X, &Q->X ) );
+    MPI_CHK( mpi_copy( &P->Y, &Q->Y ) );
+    MPI_CHK( mpi_copy( &P->Z, &Q->Z ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Import a non-zero point from ASCII strings
+ */
+int ecp_point_read_string( ecp_point *P, int radix,
+                           const char *x, const char *y )
+{
+    int ret;
+
+    MPI_CHK( mpi_read_string( &P->X, radix, x ) );
+    MPI_CHK( mpi_read_string( &P->Y, radix, y ) );
+    MPI_CHK( mpi_lset( &P->Z, 1 ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Import an ECP group from ASCII strings
+ */
+int ecp_group_read_string( ecp_group *grp, int radix,
+                           const char *p, const char *b,
+                           const char *gx, const char *gy, const char *n)
+{
+    int ret;
+
+    MPI_CHK( mpi_read_string( &grp->P, radix, p ) );
+    MPI_CHK( mpi_read_string( &grp->B, radix, b ) );
+    MPI_CHK( ecp_point_read_string( &grp->G, radix, gx, gy ) );
+    MPI_CHK( mpi_read_string( &grp->N, radix, n ) );
+
+    grp->pbits = mpi_msb( &grp->P );
+    grp->nbits = mpi_msb( &grp->N );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Export a point into unsigned binary data (SEC1 2.3.3)
+ */
+int ecp_point_write_binary( const ecp_group *grp, const ecp_point *P,
+                            int format, size_t *olen,
+                            unsigned char *buf, size_t buflen )
+{
+    int ret;
+    size_t plen;
+
+    if( format != POLARSSL_ECP_PF_UNCOMPRESSED &&
+        format != POLARSSL_ECP_PF_COMPRESSED )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    /*
+     * Common case: P == 0
+     */
+    if( mpi_cmp_int( &P->Z, 0 ) == 0 )
+    {
+        if( buflen < 1 )
+            return( POLARSSL_ERR_ECP_BUFFER_TOO_SMALL );
+
+        buf[0] = 0x00;
+        *olen = 1;
+
+        return( 0 );
+    }
+
+    plen = mpi_size( &grp->P );
+
+    if( format == POLARSSL_ECP_PF_UNCOMPRESSED )
+    {
+        *olen = 2 * plen + 1;
+
+        if( buflen < *olen )
+            return( POLARSSL_ERR_ECP_BUFFER_TOO_SMALL );
+
+        buf[0] = 0x04;
+        MPI_CHK( mpi_write_binary( &P->X, buf + 1, plen ) );
+        MPI_CHK( mpi_write_binary( &P->Y, buf + 1 + plen, plen ) );
+    }
+    else if( format == POLARSSL_ECP_PF_COMPRESSED )
+    {
+        *olen = plen + 1;
+
+        if( buflen < *olen )
+            return( POLARSSL_ERR_ECP_BUFFER_TOO_SMALL );
+
+        buf[0] = 0x02 + mpi_get_bit( &P->Y, 0 );
+        MPI_CHK( mpi_write_binary( &P->X, buf + 1, plen ) );
+    }
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Import a point from unsigned binary data (SEC1 2.3.4)
+ */
+int ecp_point_read_binary( const ecp_group *grp, ecp_point *pt,
+                           const unsigned char *buf, size_t ilen ) {
+    int ret;
+    size_t plen;
+
+    if( ilen == 1 && buf[0] == 0x00 )
+        return( ecp_set_zero( pt ) );
+
+    plen = mpi_size( &grp->P );
+
+    if( ilen != 2 * plen + 1 || buf[0] != 0x04 )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    MPI_CHK( mpi_read_binary( &pt->X, buf + 1, plen ) );
+    MPI_CHK( mpi_read_binary( &pt->Y, buf + 1 + plen, plen ) );
+    MPI_CHK( mpi_lset( &pt->Z, 1 ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Import a point from a TLS ECPoint record (RFC 4492)
+ *      struct {
+ *          opaque point <1..2^8-1>;
+ *      } ECPoint;
+ */
+int ecp_tls_read_point( const ecp_group *grp, ecp_point *pt,
+                        const unsigned char **buf, size_t buf_len )
+{
+    unsigned char data_len;
+    const unsigned char *buf_start;
+
+    /*
+     * We must have at least two bytes (1 for length, at least of for data)
+     */
+    if( buf_len < 2 )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    data_len = *(*buf)++;
+    if( data_len < 1 || data_len > buf_len - 1 )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    /*
+     * Save buffer start for read_binary and update buf
+     */
+    buf_start = *buf;
+    *buf += data_len;
+
+    return ecp_point_read_binary( grp, pt, buf_start, data_len );
+}
+
+/*
+ * Export a point as a TLS ECPoint record (RFC 4492)
+ *      struct {
+ *          opaque point <1..2^8-1>;
+ *      } ECPoint;
+ */
+int ecp_tls_write_point( const ecp_group *grp, const ecp_point *pt,
+                         int format, size_t *olen,
+                         unsigned char *buf, size_t blen )
+{
+    int ret;
+
+    /*
+     * buffer length must be at least one, for our length byte
+     */
+    if( blen < 1 )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    if( ( ret = ecp_point_write_binary( grp, pt, format,
+                    olen, buf + 1, blen - 1) ) != 0 )
+        return( ret );
+
+    /*
+     * write length to the first byte and update total length
+     */
+    buf[0] = *olen;
+    ++*olen;
+
+    return 0;
+}
+
+/*
+ * Wrapper around fast quasi-modp functions, with fall-back to mpi_mod_mpi.
+ * See the documentation of struct ecp_group.
+ */
+static int ecp_modp( mpi *N, const ecp_group *grp )
+{
+    int ret;
+
+    if( grp->modp == NULL )
+        return( mpi_mod_mpi( N, N, &grp->P ) );
+
+    if( mpi_cmp_int( N, 0 ) < 0 || mpi_msb( N ) > 2 * grp->pbits )
+        return( POLARSSL_ERR_ECP_GENERIC );
+
+    MPI_CHK( grp->modp( N ) );
+
+    while( mpi_cmp_int( N, 0 ) < 0 )
+        MPI_CHK( mpi_add_mpi( N, N, &grp->P ) );
+
+    while( mpi_cmp_mpi( N, &grp->P ) >= 0 )
+        MPI_CHK( mpi_sub_mpi( N, N, &grp->P ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * 192 bits in terms of t_uint
+ */
+#define P192_SIZE_INT   ( 192 / CHAR_BIT / sizeof( t_uint ) )
+
+/*
+ * Table to get S1, S2, S3 of FIPS 186-3 D.2.1:
+ * -1 means let this chunk be 0
+ * a positive value i means A_i.
+ */
+#define P192_CHUNKS         3
+#define P192_CHUNK_CHAR     ( 64 / CHAR_BIT )
+#define P192_CHUNK_INT      ( P192_CHUNK_CHAR / sizeof( t_uint ) )
+
+const signed char p192_tbl[][P192_CHUNKS] = {
+    { -1,   3,  3   }, /* S1 */
+    { 4,    4,  -1  }, /* S2 */
+    { 5,    5,  5   }, /* S3 */
+};
+
+/*
+ * Fast quasi-reduction modulo p192 (FIPS 186-3 D.2.1)
+ */
+static int ecp_mod_p192( mpi *N )
+{
+    int ret;
+    unsigned char i, j, offset;
+    signed char chunk;
+    mpi tmp, acc;
+    t_uint tmp_p[P192_SIZE_INT], acc_p[P192_SIZE_INT + 1];
+
+    tmp.s = 1;
+    tmp.n = sizeof( tmp_p ) / sizeof( tmp_p[0] );
+    tmp.p = tmp_p;
+
+    acc.s = 1;
+    acc.n = sizeof( acc_p ) / sizeof( acc_p[0] );
+    acc.p = acc_p;
+
+    MPI_CHK( mpi_grow( N, P192_SIZE_INT * 2 ) );
+
+    /*
+     * acc = T
+     */
+    memset( acc_p, 0, sizeof( acc_p ) );
+    memcpy( acc_p, N->p, P192_CHUNK_CHAR * P192_CHUNKS );
+
+    for( i = 0; i < sizeof( p192_tbl ) / sizeof( p192_tbl[0] ); i++)
+    {
+        /*
+         * tmp = S_i
+         */
+        memset( tmp_p, 0, sizeof( tmp_p ) );
+        for( j = 0, offset = P192_CHUNKS - 1; j < P192_CHUNKS; j++, offset-- )
+        {
+            chunk = p192_tbl[i][j];
+            if( chunk >= 0 )
+                memcpy( tmp_p + offset * P192_CHUNK_INT,
+                        N->p + chunk * P192_CHUNK_INT,
+                        P192_CHUNK_CHAR );
+        }
+
+        /*
+         * acc += tmp
+         */
+        MPI_CHK( mpi_add_abs( &acc, &acc, &tmp ) );
+    }
+
+    MPI_CHK( mpi_copy( N, &acc ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Size of p521 in terms of t_uint
+ */
+#define P521_SIZE_INT   ( 521 / CHAR_BIT / sizeof( t_uint ) + 1 )
+
+/*
+ * Bits to keep in the most significant t_uint
+ */
+#if defined(POLARSS_HAVE_INT8)
+#define P521_MASK       0x01
+#else
+#define P521_MASK       0x01FF
+#endif
+
+/*
+ * Fast quasi-reduction modulo p521 (FIPS 186-3 D.2.5)
+ */
+static int ecp_mod_p521( mpi *N )
+{
+    int ret;
+    t_uint Mp[P521_SIZE_INT];
+    mpi M;
+
+    if( N->n < P521_SIZE_INT )
+        return( 0 );
+
+    memset( Mp, 0, P521_SIZE_INT * sizeof( t_uint ) );
+    memcpy( Mp, N->p, P521_SIZE_INT * sizeof( t_uint ) );
+    Mp[P521_SIZE_INT - 1] &= P521_MASK;
+
+    M.s = 1;
+    M.n = P521_SIZE_INT;
+    M.p = Mp;
+
+    MPI_CHK( mpi_shift_r( N, 521 ) );
+
+    MPI_CHK( mpi_add_abs( N, N, &M ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Domain parameters for secp192r1
+ */
+#define SECP192R1_P \
+    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"
+#define SECP192R1_B \
+    "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"
+#define SECP192R1_GX \
+    "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012"
+#define SECP192R1_GY \
+    "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811"
+#define SECP192R1_N \
+    "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"
+
+/*
+ * Domain parameters for secp224r1
+ */
+#define SECP224R1_P \
+    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"
+#define SECP224R1_B \
+    "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"
+#define SECP224R1_GX \
+    "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21"
+#define SECP224R1_GY \
+    "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34"
+#define SECP224R1_N \
+    "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"
+
+/*
+ * Domain parameters for secp256r1
+ */
+#define SECP256R1_P \
+    "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"
+#define SECP256R1_B \
+    "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"
+#define SECP256R1_GX \
+    "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"
+#define SECP256R1_GY \
+    "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"
+#define SECP256R1_N \
+    "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"
+
+/*
+ * Domain parameters for secp384r1
+ */
+#define SECP384R1_P \
+    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" \
+    "FFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF"
+#define SECP384R1_B \
+    "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE814112" \
+    "0314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF"
+#define SECP384R1_GX \
+    "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B98" \
+    "59F741E082542A385502F25DBF55296C3A545E3872760AB7"
+#define SECP384R1_GY \
+    "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147C" \
+    "E9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F"
+#define SECP384R1_N \
+    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" \
+    "C7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973"
+
+/*
+ * Domain parameters for secp521r1
+ */
+#define SECP521R1_P \
+    "000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" \
+    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" \
+    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+#define SECP521R1_B \
+    "00000051953EB9618E1C9A1F929A21A0B68540EEA2DA725B" \
+    "99B315F3B8B489918EF109E156193951EC7E937B1652C0BD" \
+    "3BB1BF073573DF883D2C34F1EF451FD46B503F00"
+#define SECP521R1_GX \
+    "000000C6858E06B70404E9CD9E3ECB662395B4429C648139" \
+    "053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127" \
+    "A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
+#define SECP521R1_GY \
+    "0000011839296A789A3BC0045C8A5FB42C7D1BD998F54449" \
+    "579B446817AFBD17273E662C97EE72995EF42640C550B901" \
+    "3FAD0761353C7086A272C24088BE94769FD16650"
+#define SECP521R1_N \
+    "000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" \
+    "FFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148" \
+    "F709A5D03BB5C9B8899C47AEBB6FB71E91386409"
+
+/*
+ * Set a group using well-known domain parameters
+ */
+int ecp_use_known_dp( ecp_group *grp, ecp_group_id id )
+{
+    grp->id = id;
+
+    switch( id )
+    {
+        case POLARSSL_ECP_DP_SECP192R1:
+            grp->modp = ecp_mod_p192;
+            return( ecp_group_read_string( grp, 16,
+                        SECP192R1_P, SECP192R1_B,
+                        SECP192R1_GX, SECP192R1_GY, SECP192R1_N ) );
+
+        case POLARSSL_ECP_DP_SECP224R1:
+            return( ecp_group_read_string( grp, 16,
+                        SECP224R1_P, SECP224R1_B,
+                        SECP224R1_GX, SECP224R1_GY, SECP224R1_N ) );
+
+        case POLARSSL_ECP_DP_SECP256R1:
+            return( ecp_group_read_string( grp, 16,
+                        SECP256R1_P, SECP256R1_B,
+                        SECP256R1_GX, SECP256R1_GY, SECP256R1_N ) );
+
+        case POLARSSL_ECP_DP_SECP384R1:
+            return( ecp_group_read_string( grp, 16,
+                        SECP384R1_P, SECP384R1_B,
+                        SECP384R1_GX, SECP384R1_GY, SECP384R1_N ) );
+
+        case POLARSSL_ECP_DP_SECP521R1:
+            grp->modp = ecp_mod_p521;
+            return( ecp_group_read_string( grp, 16,
+                        SECP521R1_P, SECP521R1_B,
+                        SECP521R1_GX, SECP521R1_GY, SECP521R1_N ) );
+    }
+
+    return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+}
+
+/*
+ * Set a group from an ECParameters record (RFC 4492)
+ */
+int ecp_tls_read_group( ecp_group *grp, const unsigned char **buf, size_t len )
+{
+    ecp_group_id id;
+
+    /*
+     * We expect at least three bytes (see below)
+     */
+    if( len < 3 )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    /*
+     * First byte is curve_type; only named_curve is handled
+     */
+    if( *(*buf)++ != POLARSSL_ECP_TLS_NAMED_CURVE )
+        return( POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+
+    /*
+     * Next two bytes are the namedcurve value
+     */
+    id = *(*buf)++;
+    id <<= 8;
+    id |= *(*buf)++;
+    return ecp_use_known_dp( grp, id );
+}
+
+/*
+ * Write the ECParameters record corresponding to a group (RFC 4492)
+ */
+int ecp_tls_write_group( const ecp_group *grp, size_t *olen,
+                         unsigned char *buf, size_t blen )
+{
+    /*
+     * We are going to write 3 bytes (see below)
+     */
+    *olen = 3;
+    if( blen < *olen )
+        return( POLARSSL_ERR_ECP_BUFFER_TOO_SMALL );
+
+    /*
+     * First byte is curve_type, always named_curve
+     */
+    *buf++ = POLARSSL_ECP_TLS_NAMED_CURVE;
+
+    /*
+     * Next two bytes are the namedcurve value
+     */
+    buf[0] = grp->id >> 8;
+    buf[1] = grp->id & 0xFF;
+
+    return 0;
+}
+
+/*
+ * Fast mod-p functions expect their argument to be in the 0..p^2 range.
+ *
+ * In order to guarantee that, we need to ensure that operands of
+ * mpi_mul_mpi are in the 0..p range. So, after each operation we will
+ * bring the result back to this range.
+ *
+ * The following macros are shortcuts for doing that.
+ */
+
+/*
+ * Reduce a mpi mod p in-place, general case, to use after mpi_mul_mpi
+ */
+#define MOD_MUL( N )    MPI_CHK( ecp_modp( &N, grp ) )
+
+/*
+ * Reduce a mpi mod p in-place, to use after mpi_sub_mpi
+ */
+#define MOD_SUB( N )                                \
+    while( mpi_cmp_int( &N, 0 ) < 0 )               \
+        MPI_CHK( mpi_add_mpi( &N, &N, &grp->P ) )
+
+/*
+ * Reduce a mpi mod p in-place, to use after mpi_add_mpi and mpi_mul_int
+ */
+#define MOD_ADD( N )                                \
+    while( mpi_cmp_mpi( &N, &grp->P ) >= 0 )        \
+        MPI_CHK( mpi_sub_mpi( &N, &N, &grp->P ) )
+
+/*
+ * Check that a point is valid as a public key (SEC1 3.2.3.1)
+ */
+int ecp_check_pubkey( const ecp_group *grp, const ecp_point *pt )
+{
+    int ret;
+    mpi YY, RHS;
+
+    if( mpi_cmp_int( &pt->Z, 0 ) == 0 )
+        return( POLARSSL_ERR_ECP_GENERIC );
+
+    /*
+     * pt coordinates must be normalized for our checks
+     */
+    if( mpi_cmp_int( &pt->Z, 1 ) != 0 )
+        return( POLARSSL_ERR_ECP_GENERIC );
+
+    if( mpi_cmp_int( &pt->X, 0 ) < 0 ||
+        mpi_cmp_int( &pt->Y, 0 ) < 0 ||
+        mpi_cmp_mpi( &pt->X, &grp->P ) >= 0 ||
+        mpi_cmp_mpi( &pt->Y, &grp->P ) >= 0 )
+        return( POLARSSL_ERR_ECP_GENERIC );
+
+    mpi_init( &YY ); mpi_init( &RHS );
+
+    /*
+     * YY = Y^2
+     * RHS = X (X^2 - 3) + B = X^3 - 3X + B
+     */
+    MPI_CHK( mpi_mul_mpi( &YY,  &pt->Y,  &pt->Y   ) );  MOD_MUL( YY  );
+    MPI_CHK( mpi_mul_mpi( &RHS, &pt->X,  &pt->X   ) );  MOD_MUL( RHS );
+    MPI_CHK( mpi_sub_int( &RHS, &RHS,    3        ) );  MOD_SUB( RHS );
+    MPI_CHK( mpi_mul_mpi( &RHS, &RHS,    &pt->X   ) );  MOD_MUL( RHS );
+    MPI_CHK( mpi_add_mpi( &RHS, &RHS,    &grp->B  ) );  MOD_ADD( RHS );
+
+    if( mpi_cmp_mpi( &YY, &RHS ) != 0 )
+        ret = POLARSSL_ERR_ECP_GENERIC;
+
+cleanup:
+
+    mpi_free( &YY ); mpi_free( &RHS );
+
+    return( ret );
+}
+
+/*
+ * Normalize jacobian coordinates so that Z == 0 || Z == 1  (GECC 3.2.1)
+ */
+static int ecp_normalize( const ecp_group *grp, ecp_point *pt )
+{
+    int ret;
+    mpi Zi, ZZi;
+
+    if( mpi_cmp_int( &pt->Z, 0 ) == 0 )
+        return( 0 );
+
+    mpi_init( &Zi ); mpi_init( &ZZi );
+
+    /*
+     * X = X / Z^2  mod p
+     */
+    MPI_CHK( mpi_inv_mod( &Zi,      &pt->Z,     &grp->P ) );
+    MPI_CHK( mpi_mul_mpi( &ZZi,     &Zi,        &Zi     ) ); MOD_MUL( ZZi );
+    MPI_CHK( mpi_mul_mpi( &pt->X,   &pt->X,     &ZZi    ) ); MOD_MUL( pt->X );
+
+    /*
+     * Y = Y / Z^3  mod p
+     */
+    MPI_CHK( mpi_mul_mpi( &pt->Y,   &pt->Y,     &ZZi    ) ); MOD_MUL( pt->Y );
+    MPI_CHK( mpi_mul_mpi( &pt->Y,   &pt->Y,     &Zi     ) ); MOD_MUL( pt->Y );
+
+    /*
+     * Z = 1
+     */
+    MPI_CHK( mpi_lset( &pt->Z, 1 ) );
+
+cleanup:
+
+    mpi_free( &Zi ); mpi_free( &ZZi );
+
+    return( ret );
+}
+
+/*
+ * Normalize jacobian coordinates of an array of points,
+ * using Montgomery's trick to perform only one inversion mod P.
+ * (See for example Cohen's "A Course in Computational Algebraic Number
+ * Theory", Algorithm 10.3.4.)
+ *
+ * Warning: fails if one of the points is zero!
+ * This should never happen, see choice of w in ecp_mul().
+ */
+static int ecp_normalize_many( const ecp_group *grp,
+                               ecp_point T[], size_t t_len )
+{
+    int ret;
+    size_t i;
+    mpi *c, u, Zi, ZZi;
+
+    if( t_len < 2 )
+        return( ecp_normalize( grp, T ) );
+
+    if( ( c = (mpi *) malloc( t_len * sizeof( mpi ) ) ) == NULL )
+        return( POLARSSL_ERR_ECP_GENERIC );
+
+    mpi_init( &u ); mpi_init( &Zi ); mpi_init( &ZZi );
+    for( i = 0; i < t_len; i++ )
+        mpi_init( &c[i] );
+
+    /*
+     * c[i] = Z_0 * ... * Z_i
+     */
+    MPI_CHK( mpi_copy( &c[0], &T[0].Z ) );
+    for( i = 1; i < t_len; i++ )
+    {
+        MPI_CHK( mpi_mul_mpi( &c[i], &c[i-1], &T[i].Z ) );
+        MOD_MUL( c[i] );
+    }
+
+    /*
+     * u = 1 / (Z_0 * ... * Z_n) mod P
+     */
+    MPI_CHK( mpi_inv_mod( &u, &c[t_len-1], &grp->P ) );
+
+    for( i = t_len - 1; ; i-- )
+    {
+        /*
+         * Zi = 1 / Z_i mod p
+         * u = 1 / (Z_0 * ... * Z_i) mod P
+         */
+        if( i == 0 ) {
+            MPI_CHK( mpi_copy( &Zi, &u ) );
+        }
+        else
+        {
+            MPI_CHK( mpi_mul_mpi( &Zi, &u, &c[i-1] ) ); MOD_MUL( Zi );
+            MPI_CHK( mpi_mul_mpi( &u,  &u, &T[i].Z ) ); MOD_MUL( u );
+        }
+
+        /*
+         * proceed as in normalize()
+         */
+        MPI_CHK( mpi_mul_mpi( &ZZi,    &Zi,     &Zi     ) ); MOD_MUL( ZZi );
+        MPI_CHK( mpi_mul_mpi( &T[i].X, &T[i].X, &ZZi    ) ); MOD_MUL( T[i].X );
+        MPI_CHK( mpi_mul_mpi( &T[i].Y, &T[i].Y, &ZZi    ) ); MOD_MUL( T[i].Y );
+        MPI_CHK( mpi_mul_mpi( &T[i].Y, &T[i].Y, &Zi     ) ); MOD_MUL( T[i].Y );
+        MPI_CHK( mpi_lset( &T[i].Z, 1 ) );
+
+        if( i == 0 )
+            break;
+    }
+
+cleanup:
+
+    mpi_free( &u ); mpi_free( &Zi ); mpi_free( &ZZi );
+    for( i = 0; i < t_len; i++ )
+        mpi_free( &c[i] );
+    free( c );
+
+    return( ret );
+}
+
+
+/*
+ * Point doubling R = 2 P, Jacobian coordinates (GECC 3.21)
+ */
+static int ecp_double_jac( const ecp_group *grp, ecp_point *R,
+                           const ecp_point *P )
+{
+    int ret;
+    mpi T1, T2, T3, X, Y, Z;
+
+#if defined(POLARSSL_SELF_TEST)
+    dbl_count++;
+#endif
+
+    if( mpi_cmp_int( &P->Z, 0 ) == 0 )
+        return( ecp_set_zero( R ) );
+
+    mpi_init( &T1 ); mpi_init( &T2 ); mpi_init( &T3 );
+    mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z );
+
+    MPI_CHK( mpi_mul_mpi( &T1,  &P->Z,  &P->Z ) );  MOD_MUL( T1 );
+    MPI_CHK( mpi_sub_mpi( &T2,  &P->X,  &T1   ) );  MOD_SUB( T2 );
+    MPI_CHK( mpi_add_mpi( &T1,  &P->X,  &T1   ) );  MOD_ADD( T1 );
+    MPI_CHK( mpi_mul_mpi( &T2,  &T2,    &T1   ) );  MOD_MUL( T2 );
+    MPI_CHK( mpi_mul_int( &T2,  &T2,    3     ) );  MOD_ADD( T2 );
+    MPI_CHK( mpi_mul_int( &Y,   &P->Y,  2     ) );  MOD_ADD( Y  );
+    MPI_CHK( mpi_mul_mpi( &Z,   &Y,     &P->Z ) );  MOD_MUL( Z  );
+    MPI_CHK( mpi_mul_mpi( &Y,   &Y,     &Y    ) );  MOD_MUL( Y  );
+    MPI_CHK( mpi_mul_mpi( &T3,  &Y,     &P->X ) );  MOD_MUL( T3 );
+    MPI_CHK( mpi_mul_mpi( &Y,   &Y,     &Y    ) );  MOD_MUL( Y  );
+
+    /*
+     * For Y = Y / 2 mod p, we must make sure that Y is even before
+     * using right-shift. No need to reduce mod p afterwards.
+     */
+    if( mpi_get_bit( &Y, 0 ) == 1 )
+        MPI_CHK( mpi_add_mpi( &Y, &Y, &grp->P ) );
+    MPI_CHK( mpi_shift_r( &Y,   1             ) );
+
+    MPI_CHK( mpi_mul_mpi( &X,   &T2,    &T2   ) );  MOD_MUL( X  );
+    MPI_CHK( mpi_mul_int( &T1,  &T3,    2     ) );  MOD_ADD( T1 );
+    MPI_CHK( mpi_sub_mpi( &X,   &X,     &T1   ) );  MOD_SUB( X  );
+    MPI_CHK( mpi_sub_mpi( &T1,  &T3,    &X    ) );  MOD_SUB( T1 );
+    MPI_CHK( mpi_mul_mpi( &T1,  &T1,    &T2   ) );  MOD_MUL( T1 );
+    MPI_CHK( mpi_sub_mpi( &Y,   &T1,    &Y    ) );  MOD_SUB( Y  );
+
+    MPI_CHK( mpi_copy( &R->X, &X ) );
+    MPI_CHK( mpi_copy( &R->Y, &Y ) );
+    MPI_CHK( mpi_copy( &R->Z, &Z ) );
+
+cleanup:
+
+    mpi_free( &T1 ); mpi_free( &T2 ); mpi_free( &T3 );
+    mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z );
+
+    return( ret );
+}
+
+/*
+ * Addition or subtraction: R = P + Q or R = P + Q,
+ * mixed affine-Jacobian coordinates (GECC 3.22)
+ *
+ * The coordinates of Q must be normalized (= affine),
+ * but those of P don't need to. R is not normalized.
+ *
+ * If sign >= 0, perform addition, otherwise perform subtraction,
+ * taking advantage of the fact that, for Q != 0, we have
+ * -Q = (Q.X, -Q.Y, Q.Z)
+ */
+static int ecp_add_mixed( const ecp_group *grp, ecp_point *R,
+                          const ecp_point *P, const ecp_point *Q,
+                          signed char sign )
+{
+    int ret;
+    mpi T1, T2, T3, T4, X, Y, Z;
+
+#if defined(POLARSSL_SELF_TEST)
+    add_count++;
+#endif
+
+    /*
+     * Trivial cases: P == 0 or Q == 0
+     * (Check Q first, so that we know Q != 0 when we compute -Q.)
+     */
+    if( mpi_cmp_int( &Q->Z, 0 ) == 0 )
+        return( ecp_copy( R, P ) );
+
+    if( mpi_cmp_int( &P->Z, 0 ) == 0 )
+    {
+        ret = ecp_copy( R, Q );
+
+        /*
+         * -R.Y mod P = P - R.Y unless R.Y == 0
+         */
+        if( ret == 0 && sign < 0)
+            if( mpi_cmp_int( &R->Y, 0 ) != 0 )
+                ret = mpi_sub_mpi( &R->Y, &grp->P, &R->Y );
+
+        return( ret );
+    }
+
+    /*
+     * Make sure Q coordinates are normalized
+     */
+    if( mpi_cmp_int( &Q->Z, 1 ) != 0 )
+        return( POLARSSL_ERR_ECP_GENERIC );
+
+    mpi_init( &T1 ); mpi_init( &T2 ); mpi_init( &T3 ); mpi_init( &T4 );
+    mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z );
+
+    MPI_CHK( mpi_mul_mpi( &T1,  &P->Z,  &P->Z ) );  MOD_MUL( T1 );
+    MPI_CHK( mpi_mul_mpi( &T2,  &T1,    &P->Z ) );  MOD_MUL( T2 );
+    MPI_CHK( mpi_mul_mpi( &T1,  &T1,    &Q->X ) );  MOD_MUL( T1 );
+    MPI_CHK( mpi_mul_mpi( &T2,  &T2,    &Q->Y ) );  MOD_MUL( T2 );
+
+    /*
+     * For subtraction, -Q.Y should have been used instead of Q.Y,
+     * so we replace T2 by -T2, which is P - T2 mod P
+     */
+    if( sign < 0 )
+    {
+        MPI_CHK( mpi_sub_mpi( &T2, &grp->P, &T2 ) );
+        MOD_SUB( T2 );
+    }
+
+    MPI_CHK( mpi_sub_mpi( &T1,  &T1,    &P->X ) );  MOD_SUB( T1 );
+    MPI_CHK( mpi_sub_mpi( &T2,  &T2,    &P->Y ) );  MOD_SUB( T2 );
+
+    if( mpi_cmp_int( &T1, 0 ) == 0 )
+    {
+        if( mpi_cmp_int( &T2, 0 ) == 0 )
+        {
+            ret = ecp_double_jac( grp, R, P );
+            goto cleanup;
+        }
+        else
+        {
+            ret = ecp_set_zero( R );
+            goto cleanup;
+        }
+    }
+
+    MPI_CHK( mpi_mul_mpi( &Z,   &P->Z,  &T1   ) );  MOD_MUL( Z  );
+    MPI_CHK( mpi_mul_mpi( &T3,  &T1,    &T1   ) );  MOD_MUL( T3 );
+    MPI_CHK( mpi_mul_mpi( &T4,  &T3,    &T1   ) );  MOD_MUL( T4 );
+    MPI_CHK( mpi_mul_mpi( &T3,  &T3,    &P->X ) );  MOD_MUL( T3 );
+    MPI_CHK( mpi_mul_int( &T1,  &T3,    2     ) );  MOD_ADD( T1 );
+    MPI_CHK( mpi_mul_mpi( &X,   &T2,    &T2   ) );  MOD_MUL( X  );
+    MPI_CHK( mpi_sub_mpi( &X,   &X,     &T1   ) );  MOD_SUB( X  );
+    MPI_CHK( mpi_sub_mpi( &X,   &X,     &T4   ) );  MOD_SUB( X  );
+    MPI_CHK( mpi_sub_mpi( &T3,  &T3,    &X    ) );  MOD_SUB( T3 );
+    MPI_CHK( mpi_mul_mpi( &T3,  &T3,    &T2   ) );  MOD_MUL( T3 );
+    MPI_CHK( mpi_mul_mpi( &T4,  &T4,    &P->Y ) );  MOD_MUL( T4 );
+    MPI_CHK( mpi_sub_mpi( &Y,   &T3,    &T4   ) );  MOD_SUB( Y  );
+
+    MPI_CHK( mpi_copy( &R->X, &X ) );
+    MPI_CHK( mpi_copy( &R->Y, &Y ) );
+    MPI_CHK( mpi_copy( &R->Z, &Z ) );
+
+cleanup:
+
+    mpi_free( &T1 ); mpi_free( &T2 ); mpi_free( &T3 ); mpi_free( &T4 );
+    mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z );
+
+    return( ret );
+}
+
+/*
+ * Addition: R = P + Q, result's coordinates normalized
+ */
+int ecp_add( const ecp_group *grp, ecp_point *R,
+             const ecp_point *P, const ecp_point *Q )
+{
+    int ret;
+
+    MPI_CHK( ecp_add_mixed( grp, R, P, Q , 1 ) );
+    MPI_CHK( ecp_normalize( grp, R ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Subtraction: R = P - Q, result's coordinates normalized
+ */
+int ecp_sub( const ecp_group *grp, ecp_point *R,
+             const ecp_point *P, const ecp_point *Q )
+{
+    int ret;
+
+    MPI_CHK( ecp_add_mixed( grp, R, P, Q, -1 ) );
+    MPI_CHK( ecp_normalize( grp, R ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Compute a modified width-w non-adjacent form (NAF) of a number,
+ * with a fixed pattern for resistance to SPA/timing attacks,
+ * see <http://rd.springer.com/chapter/10.1007/3-540-36563-X_23>.
+ * (The resulting multiplication algorithm can also been seen as a
+ * modification of 2^w-ary multiplication, with signed coefficients,
+ * all of them odd.)
+ *
+ * Input:
+ * m must be an odd positive mpi less than w * k bits long
+ * x must be an array of k elements
+ * w must be less than a certain maximum (currently 8)
+ *
+ * The result is a sequence x[0], ..., x[k-1] with x[i] in the range
+ * - 2^(width - 1) .. 2^(width - 1) - 1 such that
+ * m = (2 * x[0] + 1) + 2^width * (2 * x[1] + 1) + ...
+ *     + 2^((k-1) * width) * (2 * x[k-1] + 1)
+ *
+ * Compared to "Algorithm SPA-resistant Width-w NAF with Odd Scalar"
+ * p. 335 of the cited reference, here we return only u, not d_w since
+ * it is known that the other d_w[j] will be 0.  Moreover, the returned
+ * string doesn't actually store u_i but x_i = u_i / 2 since it is known
+ * that u_i is odd. Also, since we always select a positive value for d
+ * mod 2^w, we don't need to check the sign of u[i-1] when the reference
+ * does. Finally, there is an off-by-one error in the reference: the
+ * last index should be k-1, not k.
+ */
+static int ecp_w_naf_fixed( signed char x[], size_t k,
+                            unsigned char w, const mpi *m )
+{
+    int ret;
+    unsigned int i, u, mask, carry;
+    mpi M;
+
+    mpi_init( &M );
+
+    MPI_CHK( mpi_copy( &M, m ) );
+    mask = ( 1 << w ) - 1;
+    carry = 1 << ( w - 1 );
+
+    for( i = 0; i < k; i++ )
+    {
+        u = M.p[0] & mask;
+
+        if( ( u & 1 ) == 0 && i > 0 )
+            x[i - 1] -= carry;
+
+        x[i] = u >> 1;
+        mpi_shift_r( &M, w );
+    }
+
+    /*
+     * We should have consumed all the bits now
+     */
+    if( mpi_cmp_int( &M, 0 ) != 0 )
+        ret = POLARSSL_ERR_ECP_GENERIC;
+
+cleanup:
+
+    mpi_free( &M );
+
+    return( ret );
+}
+
+/*
+ * Precompute odd multiples of P up to (2 * t_len - 1) P.
+ * The table is filled with T[i] = (2 * i + 1) P.
+ */
+static int ecp_precompute( const ecp_group *grp,
+                           ecp_point T[], size_t t_len,
+                           const ecp_point *P )
+{
+    int ret;
+    size_t i;
+    ecp_point PP;
+
+    ecp_point_init( &PP );
+
+    MPI_CHK( ecp_add( grp, &PP, P, P ) );
+
+    MPI_CHK( ecp_copy( &T[0], P ) );
+
+    for( i = 1; i < t_len; i++ )
+        MPI_CHK( ecp_add_mixed( grp, &T[i], &T[i-1], &PP, +1 ) );
+
+    /*
+     * T[0] = P already has normalized coordinates
+     */
+    MPI_CHK( ecp_normalize_many( grp, T + 1, t_len - 1 ) );
+
+cleanup:
+
+    ecp_point_free( &PP );
+
+    return( ret );
+}
+
+/*
+ * Maximum length of the precomputed table
+ */
+#define MAX_PRE_LEN     ( 1 << (POLARSSL_ECP_WINDOW_SIZE - 1) )
+
+/*
+ * Maximum length of the NAF: ceil( grp->nbits + 1 ) / w
+ * (that is: grp->nbits / w + 1)
+ * Allow p_bits + 1 bits in case M = grp->N + 1 is one bit longer than N.
+ */
+#define MAX_NAF_LEN     ( POLARSSL_ECP_MAX_N_BITS / 2 + 1 )
+
+/*
+ * Integer multiplication: R = m * P
+ *
+ * Based on fixed-pattern width-w NAF, see comments of ecp_w_naf_fixed()
+ * and <http://rd.springer.com/chapter/10.1007/3-540-36563-X_23>.
+ *
+ * This function executes a fixed number of operations for
+ * random m in the range 0 .. 2^nbits - 1.
+ */
+int ecp_mul( const ecp_group *grp, ecp_point *R,
+             const mpi *m, const ecp_point *P )
+{
+    int ret;
+    unsigned char w, m_is_odd;
+    size_t pre_len, naf_len, i, j;
+    signed char naf[ MAX_NAF_LEN ];
+    ecp_point Q, T[ MAX_PRE_LEN ];
+    mpi M;
+
+    if( mpi_cmp_int( m, 0 ) < 0 || mpi_msb( m ) > grp->nbits )
+        return( POLARSSL_ERR_ECP_GENERIC );
+
+    w = grp->nbits >= 521 ? 6 :
+        grp->nbits >= 224 ? 5 :
+        4;
+
+    /*
+     * Make sure w is within the limits.
+     * The last test ensures that none of the precomputed points is zero,
+     * which wouldn't be handled correctly by ecp_normalize_many().
+     * It is only useful for small curves, as used in the test suite.
+     */
+    if( w > POLARSSL_ECP_WINDOW_SIZE )
+        w = POLARSSL_ECP_WINDOW_SIZE;
+    if( w < 2 || w >= grp->nbits )
+        w = 2;
+
+    pre_len = 1 << ( w - 1 );
+    naf_len = grp->nbits / w + 1;
+
+    mpi_init( &M );
+    ecp_point_init( &Q );
+    for( i = 0; i < pre_len; i++ )
+        ecp_point_init( &T[i] );
+
+    m_is_odd = ( mpi_get_bit( m, 0 ) == 1 );
+
+    /*
+     * Make sure M is odd:
+     * later we'll get m * P by subtracting * P or 2 * P to M * P.
+     */
+    MPI_CHK( mpi_copy( &M, m ) );
+    MPI_CHK( mpi_add_int( &M, &M, 1 + m_is_odd ) );
+
+    /*
+     * Compute the fixed-pattern NAF and precompute odd multiples
+     */
+    MPI_CHK( ecp_w_naf_fixed( naf, naf_len, w, &M ) );
+    MPI_CHK( ecp_precompute( grp, T, pre_len, P ) );
+
+    /*
+     * Compute M * P, using a variant of left-to-right 2^w-ary multiplication:
+     * at each step we add (2 * naf[i] + 1) P, then multiply by 2^w.
+     *
+     * If naf[i] >= 0, we have (2 * naf[i] + 1) P == T[ naf[i] ]
+     * Otherwise, (2 * naf[i] + 1) P == - ( 2 * ( - naf[i] - 1 ) + 1) P
+     *                               == T[ - naf[i] - 1 ]
+     */
+    MPI_CHK( ecp_set_zero( &Q ) );
+    i = naf_len - 1;
+    while( 1 )
+    {
+        if( naf[i] < 0 )
+        {
+            MPI_CHK( ecp_add_mixed( grp, &Q, &Q, &T[ - naf[i] - 1 ], -1 ) );
+        }
+        else
+        {
+            MPI_CHK( ecp_add_mixed( grp, &Q, &Q, &T[ naf[i] ], +1 ) );
+        }
+
+        if( i == 0 )
+            break;
+        i--;
+
+        for( j = 0; j < w; j++ )
+        {
+            MPI_CHK( ecp_double_jac( grp, &Q, &Q ) );
+        }
+    }
+
+    /*
+     * Now get m * P from M * P.
+     * Since we don't need T[] any more, we can recycle it:
+     * we already have T[0] = P, now set T[1] = 2 * P.
+     */
+    MPI_CHK( ecp_add( grp, &T[1], P, P ) );
+    MPI_CHK( ecp_sub( grp, R, &Q, &T[m_is_odd] ) );
+
+
+cleanup:
+
+    mpi_free( &M );
+    ecp_point_free( &Q );
+    for( i = 0; i < pre_len; i++ )
+        ecp_point_free( &T[i] );
+
+    return( ret );
+}
+
+/*
+ * Generate a keypair (SEC1 3.2.1)
+ */
+int ecp_gen_keypair( const ecp_group *grp, mpi *d, ecp_point *Q,
+                     int (*f_rng)(void *, unsigned char *, size_t),
+                     void *p_rng )
+{
+    int count = 0;
+    size_t n_size = (grp->nbits + 7) / 8;
+
+    /*
+     * Generate d such that 1 <= n < N
+     */
+    do
+    {
+        mpi_fill_random( d, n_size, f_rng, p_rng );
+
+        while( mpi_cmp_mpi( d, &grp->N ) >= 0 )
+            mpi_shift_r( d, 1 );
+
+        if( count++ > 10 )
+            return( POLARSSL_ERR_ECP_GENERIC );
+    }
+    while( mpi_cmp_int( d, 1 ) < 0 );
+
+    return( ecp_mul( grp, Q, d, &grp->G ) );
+}
+
+#if defined(POLARSSL_SELF_TEST)
+
+/*
+ * Checkup routine
+ */
+int ecp_self_test( int verbose )
+{
+    int ret;
+    size_t i;
+    ecp_group grp;
+    ecp_point R;
+    mpi m;
+    unsigned long add_c_prev, dbl_c_prev;
+    char *exponents[] =
+    {
+        "000000000000000000000000000000000000000000000000", /* zero */
+        "000000000000000000000000000000000000000000000001", /* one */
+        "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", /* N */
+        "5EA6F389A38B8BC81E767753B15AA5569E1782E30ABE7D25", /* random */
+        "400000000000000000000000000000000000000000000000",
+        "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+        "555555555555555555555555555555555555555555555555",
+    };
+
+    ecp_group_init( &grp );
+    ecp_point_init( &R );
+    mpi_init( &m );
+
+    MPI_CHK( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_SECP192R1 ) );
+
+    if( verbose != 0 )
+        printf( "  ECP test #1 (SPA resistance): " );
+
+    add_count = 0;
+    dbl_count = 0;
+    MPI_CHK( mpi_read_string( &m, 16, exponents[0] ) );
+    MPI_CHK( ecp_mul( &grp, &R, &m, &grp.G ) );
+
+    for( i = 1; i < sizeof( exponents ) / sizeof( exponents[0] ); i++ )
+    {
+        add_c_prev = add_count;
+        dbl_c_prev = dbl_count;
+        add_count = 0;
+        dbl_count = 0;
+
+        MPI_CHK( mpi_read_string( &m, 16, exponents[i] ) );
+        MPI_CHK( ecp_mul( &grp, &R, &m, &grp.G ) );
+
+        if( add_count != add_c_prev || dbl_count != dbl_c_prev )
+        {
+            if( verbose != 0 )
+                printf( "failed (%zu)\n", i );
+
+            ret = 1;
+            goto cleanup;
+        }
+    }
+
+    if( verbose != 0 )
+        printf( "passed\n" );
+
+cleanup:
+
+    if( ret < 0 && verbose != 0 )
+        printf( "Unexpected error, return code = %08X\n", ret );
+
+    ecp_group_free( &grp );
+    ecp_point_free( &R );
+    mpi_free( &m );
+
+    if( verbose != 0 )
+        printf( "\n" );
+
+    return( ret );
+}
+
+#endif
+
+#endif
diff --git a/library/error.c b/library/error.c
index d4bc277..1a8457b 100644
--- a/library/error.c
+++ b/library/error.c
@@ -63,6 +63,10 @@
 #include "polarssl/dhm.h"
 #endif
 
+#if defined(POLARSSL_ECP_C)
+#include "polarssl/ecp.h"
+#endif
+
 #if defined(POLARSSL_ENTROPY_C)
 #include "polarssl/entropy.h"
 #endif
@@ -182,6 +186,13 @@
             snprintf( buf, buflen, "DHM - Calculation of the DHM secret failed" );
 #endif /* POLARSSL_DHM_C */
 
+#if defined(POLARSSL_ECP_C)
+        if( use_ret == -(POLARSSL_ERR_ECP_BAD_INPUT_DATA) )
+            snprintf( buf, buflen, "ECP - Bad input parameters to function" );
+        if( use_ret == -(POLARSSL_ERR_ECP_GENERIC) )
+            snprintf( buf, buflen, "ECP -  Generic ECP error" );
+#endif /* POLARSSL_ECP_C */
+
 #if defined(POLARSSL_MD_C)
         if( use_ret == -(POLARSSL_ERR_MD_FEATURE_UNAVAILABLE) )
             snprintf( buf, buflen, "MD - The selected feature is not available" );
diff --git a/programs/.gitignore b/programs/.gitignore
index d6d9efc..9a69532 100644
--- a/programs/.gitignore
+++ b/programs/.gitignore
@@ -29,6 +29,7 @@
 ssl/ssl_server
 ssl/ssl_server2
 test/benchmark
+test/ecp-bench
 test/o_p_test
 test/selftest
 test/ssl_cert_test
diff --git a/programs/Makefile b/programs/Makefile
index 9b0d1f5..388b029 100644
--- a/programs/Makefile
+++ b/programs/Makefile
@@ -38,6 +38,7 @@
 	random/gen_random_ctr_drbg				\
 	test/ssl_cert_test	test/benchmark		\
 	test/selftest		test/ssl_test		\
+	test/ecp-bench							\
 	util/strerror							\
 	x509/cert_app		x509/crl_app		\
 	x509/cert_req
@@ -174,6 +175,10 @@
 	echo   "  CC    test/benchmark.c"
 	$(CC) $(CFLAGS) $(OFLAGS) test/benchmark.c   $(LDFLAGS) -o $@
 
+test/ecp-bench: test/ecp-bench.c ../library/libpolarssl.a
+	echo   "  CC    test/ecp-bench.c"
+	$(CC) $(CFLAGS) $(OFLAGS) test/ecp-bench.c   $(LDFLAGS) -o $@
+
 test/selftest: test/selftest.c ../library/libpolarssl.a
 	echo   "  CC    test/selftest.c"
 	$(CC) $(CFLAGS) $(OFLAGS) test/selftest.c    $(LDFLAGS) -o $@
diff --git a/programs/test/CMakeLists.txt b/programs/test/CMakeLists.txt
index c05b903..56bcae0 100644
--- a/programs/test/CMakeLists.txt
+++ b/programs/test/CMakeLists.txt
@@ -18,6 +18,9 @@
 add_executable(benchmark benchmark.c)
 target_link_libraries(benchmark ${libs})
 
+add_executable(ecp-bench ecp-bench.c)
+target_link_libraries(ecp-bench ${libs})
+
 add_executable(ssl_test ssl_test.c)
 target_link_libraries(ssl_test ${libs})
 
diff --git a/programs/test/benchmark.c b/programs/test/benchmark.c
index 86de2c6..7ee465f 100644
--- a/programs/test/benchmark.c
+++ b/programs/test/benchmark.c
@@ -45,6 +45,7 @@
 #include "polarssl/camellia.h"
 #include "polarssl/gcm.h"
 #include "polarssl/rsa.h"
+#include "polarssl/dhm.h"
 #include "polarssl/timing.h"
 #include "polarssl/havege.h"
 #include "polarssl/ctr_drbg.h"
@@ -121,6 +122,10 @@
 #if defined(POLARSSL_CTR_DRBG_C)
     ctr_drbg_context    ctr_drbg;
 #endif
+#if defined(POLARSSL_DHM_C) && defined(POLARSSL_BIGNUM_C)
+    dhm_context dhm;
+    size_t olen = BUFSIZE;
+#endif
     ((void) argc);
     ((void) argv);
 
@@ -505,6 +510,74 @@
     rsa_free( &rsa );
 #endif
 
+#if defined(POLARSSL_DHM_C) && defined(POLARSSL_BIGNUM_C)
+    memset( &dhm, 0, sizeof( dhm_context ) );
+
+    mpi_read_string( &dhm.P, 16, POLARSSL_DHM_RFC5114_MODP_1024_P );
+    mpi_read_string( &dhm.G, 16, POLARSSL_DHM_RFC5114_MODP_1024_G );
+    dhm.len = mpi_size( &dhm.P );
+    dhm_make_public( &dhm, dhm.len, buf, dhm.len, myrand, NULL );
+    mpi_copy( &dhm.GY, &dhm.GX );
+
+    printf( HEADER_FORMAT, "DHM-1024" );
+    fflush( stdout );
+    set_alarm( 3 );
+
+    for( i = 1; ! alarmed; i++ )
+    {
+        dhm_make_public( &dhm, dhm.len, buf, dhm.len, myrand, NULL );
+        dhm_calc_secret( &dhm, buf, &olen );
+    }
+
+    printf( "%9lu handshake/s\n", i / 3 );
+
+    dhm_free( &dhm );
+
+    memset( &dhm, 0, sizeof( dhm_context ) );
+
+    mpi_read_string( &dhm.P, 16, POLARSSL_DHM_RFC3526_MODP_2048_P );
+    mpi_read_string( &dhm.G, 16, POLARSSL_DHM_RFC3526_MODP_2048_G );
+    dhm.len = mpi_size( &dhm.P );
+    dhm_make_public( &dhm, dhm.len, buf, dhm.len, myrand, NULL );
+    mpi_copy( &dhm.GY, &dhm.GX );
+
+    printf( HEADER_FORMAT, "DHM-2048" );
+    fflush( stdout );
+    set_alarm( 3 );
+
+    for( i = 1; ! alarmed; i++ )
+    {
+        dhm_make_public( &dhm, dhm.len, buf, dhm.len, myrand, NULL );
+        dhm_calc_secret( &dhm, buf, &olen );
+    }
+
+    printf( "%9lu handshake/s\n", i / 3 );
+
+    dhm_free( &dhm );
+
+    memset( &dhm, 0, sizeof( dhm_context ) );
+
+    mpi_read_string( &dhm.P, 16, POLARSSL_DHM_RFC3526_MODP_3072_P );
+    mpi_read_string( &dhm.G, 16, POLARSSL_DHM_RFC3526_MODP_3072_G );
+    dhm.len = mpi_size( &dhm.P );
+    dhm_make_public( &dhm, dhm.len, buf, dhm.len, myrand, NULL );
+    mpi_copy( &dhm.GY, &dhm.GX );
+
+    printf( HEADER_FORMAT, "DHM-3072" );
+    fflush( stdout );
+    set_alarm( 3 );
+
+    for( i = 1; ! alarmed; i++ )
+    {
+        dhm_make_public( &dhm, dhm.len, buf, dhm.len, myrand, NULL );
+        dhm_calc_secret( &dhm, buf, &olen );
+    }
+
+    printf( "%9lu handshake/s\n", i / 3 );
+
+    dhm_free( &dhm );
+#endif
+
     printf( "\n" );
 
 #if defined(_WIN32)
diff --git a/programs/test/ecp-bench.c b/programs/test/ecp-bench.c
new file mode 100644
index 0000000..3064cc9
--- /dev/null
+++ b/programs/test/ecp-bench.c
@@ -0,0 +1,205 @@
+/*
+ * Temporary mini-benchmark for ECP multiplication
+ * (and comparision with GF(p) exponentation if any argument is given)
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "polarssl/config.h"
+
+#include "polarssl/dhm.h"
+#include "polarssl/ecp.h"
+#include "polarssl/timing.h"
+#include "polarssl/error.h"
+
+#if !defined(POLARSSL_TIMING_C) || !defined(POLARSSL_BIGNUM_C) ||   \
+    !defined(POLARSSL_DHM_C) || !defined(POLARSSL_ECP_C)
+
+int main( int argc, char *argv[] )
+{
+    ((void) argc);
+    ((void) argv);
+
+    printf("POLARSSL_{TIMING,BIGNUM,DHM,ECP}_C not defined.\n");
+    return( 0 );
+}
+
+#else
+
+void dhm_bench_case( const char *s, const char *p,
+                     const char *g, const char *x )
+{
+    unsigned long i;
+    mpi P, G, X, R, C;
+
+    mpi_init( &P ); mpi_init( &G ); mpi_init( &X ); mpi_init( &R );
+    mpi_init( &C );
+
+    mpi_read_string( &P, 16, p );
+    mpi_read_string( &G, 16, g );
+    mpi_read_string( &X, 16, x );
+
+    printf( " MODP-%s : ", s );
+    fflush( stdout );
+    set_alarm( 3 );
+
+    for( i = 1; ! alarmed; i++ )
+        mpi_exp_mod( &R, &G, &X, &P, &C );
+
+    printf( "%9lu exp/s\n", i / 3 );
+
+    mpi_free( &P ); mpi_free( &G ); mpi_free( &X ); mpi_free( &R );
+    mpi_free( &C );
+}
+
+#define MODP_1024_G \
+    "205BE4D89282CE6A9488626CA9BCC1264E7C9525903F049D" \
+    "D6A77315AA12B01467EB1EA21B25569BECEC276623859A8C" \
+    "518D445DF43DCBA1B75D23B6BB6B96084FA3C10CA8DC1A96" \
+    "77E26332152233E13F4E6BF3C5D48514BC264A35AF385463" \
+    "8B8AE5E363BD6AE0B5D44875BB60A73A5E4075FB2037B833" \
+    "DC382EA6DD458EAE"
+
+#define MODP_1024_X \
+    "262F6727B2E44AF61981AAAB2AA749F64FD79AD18A1FC999" \
+    "2E71795130CB69F89BCFB70DB2A0F2A49A825A0AAA50715D" \
+    "5D9AE98198F0522AECBF8A0A2D331AB0296110617FC685FF" \
+    "7338F4201329ABDFDEAEC779E4D56CC46A0A38B18DC18DFE" \
+    "640865426A7360BCCD4E4F82B3686B4804071D4B9F5582ED" \
+    "E4758F170C3A1415"
+
+#define MODP_2048_G \
+    "717892E6698EB3CD62113AAE793EA6690791F89290FD83C3" \
+    "A059F328C5FBD10AB25C1E8F96245836600FC7772AFB42ED" \
+    "F7A809FC3D38F7794574CAC8A318D41C917EE34C81EA8838" \
+    "BA6068E81ED3C7F039A83914D044AB2379B3C5C24F0FFD4E" \
+    "B6642F26F1EDF015AB7829E9EDC8DCD8A4013C76682338E8" \
+    "3D44B0F4E870CF3134874EAA3AC0803DA2CE30922E251B02" \
+    "BDDD94137F9DFD41627AE20B7E4137BA94496A4B2AEB0354" \
+    "8E1C4126A87240B43E23BB574D354EF1AD01FED5627F240C" \
+    "2F4F5521D2F51DFE0702D18EED6DDF022D93554E7AB67D81" \
+    "AD4C5256A6AC00DE1AD0751FDCC17F8C7136C194F48A39F5" \
+    "957B831081C9058CA0C3A03C7C41F1C9"
+
+#define MODP_2048_X \
+    "AFA7186FC7A0370DE6E3DD95473C9D10C916BD186E41FB48" \
+    "195D123E5CFE8D237A2A640DCE19B3DD7028C6168084A61D" \
+    "4E4E218A1D98E0C2C93E2E1516F97E596B50C811CE93417B" \
+    "905DA6833D1CE41838AEAA9CA6CE36E2430A244A6696E99B" \
+    "54B6AAC75D4F6FF6F0568145BDBBC83F5F603D698198A8EE" \
+    "2098F5DCABDD458C465E795520EDD46C3B992EAFACF24A56" \
+    "263850C9A07165A0CE52B7DC71489CA67AC4A6E6176B547B" \
+    "A1C233E3C7898C283E82B3E74B9056F84F7B4A10B5CE10FF" \
+    "7D43107B68F2381E64C77C61474DE8CA70EA8DF935D43CD2" \
+    "CE208D9F7E468F07A93A74676ED1A3C77CB1CD59E96DCA77" \
+    "125DE5B672798FC943048FDBB24167B1"
+
+#define MODP_3072_G \
+    "38B2B12253661411B63E62F3FAC5BE7CF6A416F249E4A039" \
+    "0884765C18F2EDF93FCC2646B888ED64B783765D576887B1" \
+    "74ABB76123ECA6537DDC1F95757645F3C870CBFF2D40E40D" \
+    "C2556A14115C37BE299BDCB0F07AB7AAE0D8FA6CF44A0C22" \
+    "ED5368922EF028EC2B5B0AB42F858D5D780828A508C252A7" \
+    "96F8CE7AA9C83D1E820F6991771AAA2286A7754FCCB3AA16" \
+    "51D840EC788C5DDEAFAAC0C82EAC84EBFF5EB52699BE4020" \
+    "F62607937EB168766BCC1F786F4FCDDBB66C0BCEA983AE87" \
+    "5C2DD6C201FBF5C178308E64831D14F12399F21761D13BC1" \
+    "20B2377FBAD73613FA7B7E443BEE3879E7EEFB0A625A0EBA" \
+    "2D379E8F5EA62C91BC58A6FCDD5D7435B0E747B7B65A47BE" \
+    "0CA769DE340E611A98A6DDED72E89486875F02DAE24715E8" \
+    "3787A5DB53AA28D3F838E7F11EE80F0310C8597B585D6B1A" \
+    "3991FBD7459ED277C34C9A4AE0AE6DC7EE5C99B14F366129" \
+    "C3747617B724A2BED48F5BF3106B40E60CC3677780F1A39D" \
+    "430154C2B460CC676AACC7B34F792621D743D1AC59EDBAC6"
+
+#define MODP_3072_X \
+    "4391157AD824806F07421C1545AA795E26B120A5CEE427F0" \
+    "6725BB247E6B2F4C4BE052E4B240B2DD6194B37F3423FA3F" \
+    "00C3DF9F4C2165662FCCC9B8DE52C19001E8D54E9053A605" \
+    "24BCA6AB0698632EB5D62451987E89AF0FA63479D344E2A7" \
+    "58ED8DB5D287600C85A8DD08D9E2E5587177AB6A25A76C71" \
+    "5BCAA737FB7DC45E371F4DEC13B40CA9D461A99A98847E1B" \
+    "D9CD1BA02DCC331B5B2256887B7F0139A9E7BEA8A6CDA318" \
+    "B95E12A1CEE6A26EC070C34C270067C79DBC906295C3C9DF" \
+    "2FA6D6B637B7CE81B535DB2D6E196F46ED2F92F3EE8EACE1" \
+    "002349D035550A44B75E76B7A77A6A670EC13E334201B543" \
+    "B1708295F33B777BD4555A032313C3388DBD5ABC5FB0FEFD" \
+    "A9A98C0CC4C7E919927BDC8D46B5347A1266A4C8331C2EB0" \
+    "C93DB67244BD6CE2757BA61A063DDD0DF0561A97786D1299" \
+    "EBE3EAC838F420D1A0DC9D633B2F16CCE91E436C0413CE6A" \
+    "F98E0273FC5C08A4EA70D6DC09A1855AFB402E02BD9F261E" \
+    "863717A552F4A83D4DD5060CB70E2D4D7FFAEE912C2C4408"
+
+void dhm_bench( void )
+{
+    dhm_bench_case( "1024", POLARSSL_DHM_RFC5114_MODP_1024_P,
+            MODP_1024_G, MODP_1024_X );
+
+    dhm_bench_case( "2048", POLARSSL_DHM_RFC3526_MODP_2048_P,
+            MODP_2048_G, MODP_2048_X );
+
+    dhm_bench_case( "3072", POLARSSL_DHM_RFC3526_MODP_3072_P,
+            MODP_3072_G, MODP_3072_X );
+}
+
+void ecp_bench_case( size_t dp, char *s, char *m )
+{
+    unsigned long i;
+    ecp_group grp;
+    ecp_point R;
+    mpi M;
+
+    ecp_group_init( &grp ); ecp_point_init( &R ); mpi_init( &M );
+
+    ecp_use_known_dp( &grp, dp );
+    mpi_read_string( &M, 16, m );
+
+    printf( "  ECP-%s  : ", s );
+    fflush( stdout );
+    set_alarm( 3 );
+
+    for( i = 1; ! alarmed; i++ )
+        ecp_mul( &grp, &R, &M, &grp.G );
+
+    printf( "%9lu mul/s\n", i / 3 );
+
+    ecp_group_free( &grp ); ecp_point_free( &R ); mpi_free( &M );
+}
+
+#define ECP_192_M \
+    "FE61330B02915F25970C2AF8575CBB227E6B6394774E6EC8"
+#define ECP_224_M \
+    "EAC63628087E32F61A383DCAED57E4348081FF3637D95B904C2FD487"
+#define ECP_256_M \
+    "83C4106612BDDB781DD446CF2A873D78AA0620B9003789244A18F83B39B3EA63"
+#define ECP_384_M \
+    "3E45569675A7E2E0E2E663A11E4226FBEF0F99A96CD6E547" \
+    "9ABDDF36DD9AB7278BCD8B96A0775847A6431D2AF7476CD9"
+#define ECP_521_M \
+    "017F540D09F24EC6B102E8E4A9F14B850442D98C68FB29A6B09B9B9D40E2A750" \
+    "7F3D2D6C5B6536B607EF2BCEA4797BB3A68F0D745410EB5CFFC7FF7FB17381544E"
+
+void ecp_bench( void )
+{
+    ecp_bench_case( 0, "192", ECP_192_M );
+    ecp_bench_case( 1, "224", ECP_224_M );
+    ecp_bench_case( 2, "256", ECP_256_M );
+    ecp_bench_case( 3, "384", ECP_384_M );
+    ecp_bench_case( 4, "521", ECP_521_M );
+}
+
+int main( int argc, char *argv[] )
+{
+    ((void) argv);
+    if( argc > 1 )
+    {
+        dhm_bench();
+    }
+
+    ecp_bench();
+
+    return( 0 );
+}
+#endif
diff --git a/programs/test/selftest.c b/programs/test/selftest.c
index 8dddf34..333e3a1 100644
--- a/programs/test/selftest.c
+++ b/programs/test/selftest.c
@@ -50,6 +50,7 @@
 #include "polarssl/x509.h"
 #include "polarssl/xtea.h"
 #include "polarssl/pbkdf2.h"
+#include "polarssl/ecp.h"
 
 int main( int argc, char *argv[] )
 {
@@ -155,6 +156,11 @@
         return( ret );
 #endif
 
+#if defined(POLARSSL_ECP_C)
+    if( ( ret = ecp_self_test( v ) ) != 0 )
+        return( ret );
+#endif
+
 #else
     printf( " POLARSSL_SELF_TEST not defined.\n" );
 #endif
diff --git a/scripts/generate_errors.pl b/scripts/generate_errors.pl
index e894964..8184d78 100755
--- a/scripts/generate_errors.pl
+++ b/scripts/generate_errors.pl
@@ -12,7 +12,7 @@
                           "BASE64", "XTEA", "PBKDF2",
                           "PADLOCK", "DES", "NET", "CTR_DRBG", "ENTROPY",
                           "MD2", "MD4", "MD5", "SHA1", "SHA2", "SHA4", "GCM" );
-my @high_level_modules = ( "PEM", "X509", "DHM", "RSA", "MD", "CIPHER", "SSL" );
+my @high_level_modules = ( "PEM", "X509", "DHM", "RSA", "ECP", "MD", "CIPHER", "SSL" );
 
 my $line_separator = $/;
 undef $/;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 18d5ad4..63224b2 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -45,6 +45,9 @@
 add_test_suite(debug)
 add_test_suite(des)
 add_test_suite(dhm)
+add_test_suite(ecp)
+add_test_suite(ecdh)
+add_test_suite(ecdsa)
 add_test_suite(error)
 add_test_suite(gcm gcm.encrypt)
 add_test_suite(gcm gcm.decrypt)
diff --git a/tests/Makefile b/tests/Makefile
index 4c97aa5..08173e1 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -30,6 +30,8 @@
 		test_suite_cipher.des	test_suite_cipher.null	\
 		test_suite_ctr_drbg		test_suite_debug		\
 		test_suite_des			test_suite_dhm			\
+		test_suite_ecdh			test_suite_ecdsa		\
+		test_suite_ecp									\
 		test_suite_error		test_suite_gcm.decrypt	\
 		test_suite_gcm.decrypt	test_suite_hmac_shax	\
 		test_suite_md			test_suite_mdx			\
@@ -127,6 +129,18 @@
 	echo   "  CC    	$@.c"
 	$(CC) $(CFLAGS) $(OFLAGS) $@.c	$(LDFLAGS) -o $@
 
+test_suite_ecdh: test_suite_ecdh.c ../library/libpolarssl.a
+	echo   "  CC    	$@.c"
+	$(CC) $(CFLAGS) $(OFLAGS) $@.c	$(LDFLAGS) -o $@
+
+test_suite_ecdsa: test_suite_ecdsa.c ../library/libpolarssl.a
+	echo   "  CC    	$@.c"
+	$(CC) $(CFLAGS) $(OFLAGS) $@.c	$(LDFLAGS) -o $@
+
+test_suite_ecp: test_suite_ecp.c ../library/libpolarssl.a
+	echo   "  CC    	$@.c"
+	$(CC) $(CFLAGS) $(OFLAGS) $@.c	$(LDFLAGS) -o $@
+
 test_suite_error: test_suite_error.c ../library/libpolarssl.a
 	echo   "  CC    	$@.c"
 	$(CC) $(CFLAGS) $(OFLAGS) $@.c	$(LDFLAGS) -o $@
diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function
index 2f1b26c..208e7e6 100644
--- a/tests/suites/helpers.function
+++ b/tests/suites/helpers.function
@@ -218,3 +218,49 @@
 
     return( 0 );
 }
+
+/**
+ * This function returns a buffer given as a hex string.
+ *
+ * The buffer is reversed so that the following are equivalent:
+ *   mpi_fill_random( x, len, not_rnd, str );
+ *   mpi_read_string( x, 16, str );
+ * (So, not random at all. Usefull to match test vectors.)
+ * Based on unhexify(), just reversed (changes marked by "sic")
+ */
+static int not_rnd( void *in, unsigned char *out, size_t len )
+{
+    unsigned char *obuf;
+    const char *ibuf = in;
+    unsigned char c, c2;
+    assert( len == strlen(ibuf) / 2 );
+    assert(!(strlen(ibuf) %1)); // must be even number of bytes
+
+    obuf = out + (len - 1); // sic
+    while (*ibuf != 0)
+    {
+        c = *ibuf++;
+        if( c >= '0' && c <= '9' )
+            c -= '0';
+        else if( c >= 'a' && c <= 'f' )
+            c -= 'a' - 10;
+        else if( c >= 'A' && c <= 'F' )
+            c -= 'A' - 10;
+        else
+            assert( 0 );
+
+        c2 = *ibuf++;
+        if( c2 >= '0' && c2 <= '9' )
+            c2 -= '0';
+        else if( c2 >= 'a' && c2 <= 'f' )
+            c2 -= 'a' - 10;
+        else if( c2 >= 'A' && c2 <= 'F' )
+            c2 -= 'A' - 10;
+        else
+            assert( 0 );
+
+        *obuf-- = ( c << 4 ) | c2; // sic
+    }
+
+    return( 0 );
+}
diff --git a/tests/suites/test_suite_ecdh.data b/tests/suites/test_suite_ecdh.data
new file mode 100644
index 0000000..47b68fb
--- /dev/null
+++ b/tests/suites/test_suite_ecdh.data
@@ -0,0 +1,29 @@
+ECDH primitive random #1
+ecdh_primitive_random:SECP192R1
+
+ECDH primitive random #2
+ecdh_primitive_random:SECP224R1
+
+ECDH primitive random #3
+ecdh_primitive_random:SECP256R1
+
+ECDH primitive random #4
+ecdh_primitive_random:SECP384R1
+
+ECDH primitive random #5
+ecdh_primitive_random:SECP521R1
+
+ECDH primitive rfc 5903 p256
+ecdh_primitive_testvec:SECP256R1:C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433:DAD0B65394221CF9B051E1FECA5787D098DFE637FC90B9EF945D0C3772581180:5271A0461CDB8252D61F1C456FA3E59AB1F45B33ACCF5F58389E0577B8990BB3:C6EF9C5D78AE012A011164ACB397CE2088685D8F06BF9BE0B283AB46476BEE53:D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF63:56FBF3CA366CC23E8157854C13C58D6AAC23F046ADA30F8353E74F33039872AB:D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE
+
+ECDH primitive rfc 5903 p384
+ecdh_primitive_testvec:SECP384R1:099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194:667842D7D180AC2CDE6F74F37551F55755C7645C20EF73E31634FE72B4C55EE6DE3AC808ACB4BDB4C88732AEE95F41AA:9482ED1FC0EEB9CAFC4984625CCFC23F65032149E0E144ADA024181535A0F38EEB9FCFF3C2C947DAE69B4C634573A81C:41CB0779B4BDB85D47846725FBEC3C9430FAB46CC8DC5060855CC9BDA0AA2942E0308312916B8ED2960E4BD55A7448FC:E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571:DCFBEC7AACF3196472169E838430367F66EEBE3C6E70C416DD5F0C68759DD1FFF83FA40142209DFF5EAAD96DB9E6386C:11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746
+
+ECDH primitive rfc 5903 p521
+ecdh_primitive_testvec:SECP521R1:0037ADE9319A89F4DABDB3EF411AACCCA5123C61ACAB57B5393DCE47608172A095AA85A30FE1C2952C6771D937BA9777F5957B2639BAB072462F68C27A57382D4A52:0015417E84DBF28C0AD3C278713349DC7DF153C897A1891BD98BAB4357C9ECBEE1E3BF42E00B8E380AEAE57C2D107564941885942AF5A7F4601723C4195D176CED3E:017CAE20B6641D2EEB695786D8C946146239D099E18E1D5A514C739D7CB4A10AD8A788015AC405D7799DC75E7B7D5B6CF2261A6A7F1507438BF01BEB6CA3926F9582:0145BA99A847AF43793FDD0E872E7CDFA16BE30FDC780F97BCCC3F078380201E9C677D600B343757A3BDBF2A3163E4C2F869CCA7458AA4A4EFFC311F5CB151685EB9:00D0B3975AC4B799F5BEA16D5E13E9AF971D5E9B984C9F39728B5E5739735A219B97C356436ADC6E95BB0352F6BE64A6C2912D4EF2D0433CED2B6171640012D9460F:015C68226383956E3BD066E797B623C27CE0EAC2F551A10C2C724D9852077B87220B6536C5C408A1D2AEBB8E86D678AE49CB57091F4732296579AB44FCD17F0FC56A:01144C7D79AE6956BC8EDB8E7C787C4521CB086FA64407F97894E5E6B2D79B04D1427E73CA4BAA240A34786859810C06B3C715A3A8CC3151F2BEE417996D19F3DDEA
+
+ECDH exchange #1
+ecdh_exchange:SECP192R1
+
+ECDH exchange #2
+ecdh_exchange:SECP521R1
diff --git a/tests/suites/test_suite_ecdh.function b/tests/suites/test_suite_ecdh.function
new file mode 100644
index 0000000..105f99c
--- /dev/null
+++ b/tests/suites/test_suite_ecdh.function
@@ -0,0 +1,114 @@
+BEGIN_HEADER
+#include <polarssl/ecdh.h>
+END_HEADER
+
+BEGIN_DEPENDENCIES
+depends_on:POLARSSL_ECDH_C:POLARSSL_ECP_C:POLARSSL_BIGNUM_C
+END_DEPENDENCIES
+
+BEGIN_CASE
+ecdh_primitive_random:id
+{
+    ecp_group grp;
+    ecp_point qA, qB;
+    mpi dA, dB, zA, zB;
+    rnd_pseudo_info rnd_info;
+
+    ecp_group_init( &grp );
+    ecp_point_init( &qA ); ecp_point_init( &qB );
+    mpi_init( &dA ); mpi_init( &dB );
+    mpi_init( &zA ); mpi_init( &zB );
+    memset( &rnd_info, 0x00, sizeof( rnd_pseudo_info ) );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    TEST_ASSERT( ecdh_gen_public( &grp, &dA, &qA, &rnd_pseudo_rand, &rnd_info )
+                 == 0 );
+    TEST_ASSERT( ecdh_gen_public( &grp, &dB, &qB, &rnd_pseudo_rand, &rnd_info )
+                 == 0 );
+    TEST_ASSERT( ecdh_compute_shared( &grp, &zA, &qB, &dA ) == 0 );
+    TEST_ASSERT( ecdh_compute_shared( &grp, &zB, &qA, &dB ) == 0 );
+
+    TEST_ASSERT( mpi_cmp_mpi( &zA, &zB ) == 0 );
+
+    ecp_group_free( &grp );
+    ecp_point_free( &qA ); ecp_point_free( &qB );
+    mpi_free( &dA ); mpi_free( &dB );
+    mpi_free( &zA ); mpi_free( &zB );
+}
+END_CASE
+
+BEGIN_CASE
+ecdh_primitive_testvec:id:dA:xA:yA:dB:xB:yB:z
+{
+    ecp_group grp;
+    ecp_point qA, qB;
+    mpi dA, dB, zA, zB, check;
+
+    ecp_group_init( &grp );
+    ecp_point_init( &qA ); ecp_point_init( &qB );
+    mpi_init( &dA ); mpi_init( &dB );
+    mpi_init( &zA ); mpi_init( &zB ); mpi_init( &check );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    TEST_ASSERT( ecdh_gen_public( &grp, &dA, &qA, &not_rnd, "{dA}" ) == 0 );
+    TEST_ASSERT( ! ecp_is_zero( &qA ) );
+    TEST_ASSERT( mpi_read_string( &check, 16, "{xA}" ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &qA.X, &check ) == 0 );
+    TEST_ASSERT( mpi_read_string( &check, 16, "{yA}" ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &qA.Y, &check ) == 0 );
+
+    TEST_ASSERT( ecdh_gen_public( &grp, &dB, &qB, &not_rnd, "{dB}" ) == 0 );
+    TEST_ASSERT( ! ecp_is_zero( &qB ) );
+    TEST_ASSERT( mpi_read_string( &check, 16, "{xB}" ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &qB.X, &check ) == 0 );
+    TEST_ASSERT( mpi_read_string( &check, 16, "{yB}" ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &qB.Y, &check ) == 0 );
+
+    TEST_ASSERT( mpi_read_string( &check, 16, "{z}" ) == 0 );
+    TEST_ASSERT( ecdh_compute_shared( &grp, &zA, &qB, &dA ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &zA, &check ) == 0 );
+    TEST_ASSERT( ecdh_compute_shared( &grp, &zB, &qA, &dB ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &zB, &check ) == 0 );
+
+    ecp_group_free( &grp );
+    ecp_point_free( &qA ); ecp_point_free( &qB );
+    mpi_free( &dA ); mpi_free( &dB );
+    mpi_free( &zA ); mpi_free( &zB ); mpi_free( &check );
+}
+END_CASE
+
+BEGIN_CASE
+ecdh_exchange:id
+{
+    ecdh_context srv, cli;
+    unsigned char buf[1000];
+    const unsigned char *vbuf;
+    size_t len;
+    rnd_pseudo_info rnd_info;
+
+    ecdh_init( &srv );
+    ecdh_init( &cli );
+    memset( &rnd_info, 0x00, sizeof( rnd_pseudo_info ) );
+
+    TEST_ASSERT( ecp_use_known_dp( &srv.grp, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    memset( buf, 0x00, sizeof( buf ) ); vbuf = buf;
+    TEST_ASSERT( ecdh_make_params( &srv, &len, buf, 1000,
+                                   &rnd_pseudo_rand, &rnd_info ) == 0 );
+    TEST_ASSERT( ecdh_read_params( &cli, &vbuf, buf + len ) == 0 );
+
+    memset( buf, 0x00, sizeof( buf ) );
+    TEST_ASSERT( ecdh_make_public( &cli, &len, buf, 1000,
+                                   &rnd_pseudo_rand, &rnd_info ) == 0 );
+    TEST_ASSERT( ecdh_read_public( &srv, buf, len ) == 0 );
+
+    TEST_ASSERT( ecdh_calc_secret( &srv, &len, buf, 1000 ) == 0 );
+    TEST_ASSERT( ecdh_calc_secret( &cli, &len, buf, 1000 ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &srv.z, &cli.z ) == 0 );
+
+    ecdh_free( &srv );
+    ecdh_free( &cli );
+}
+END_CASE
diff --git a/tests/suites/test_suite_ecdsa.data b/tests/suites/test_suite_ecdsa.data
new file mode 100644
index 0000000..5a94419
--- /dev/null
+++ b/tests/suites/test_suite_ecdsa.data
@@ -0,0 +1,24 @@
+ECDSA primitive random #1
+ecdsa_prim_random:SECP192R1
+
+ECDSA primitive random #2
+ecdsa_prim_random:SECP224R1
+
+ECDSA primitive random #3
+ecdsa_prim_random:SECP256R1
+
+ECDSA primitive random #4
+ecdsa_prim_random:SECP384R1
+
+ECDSA primitive random #5
+ecdsa_prim_random:SECP521R1
+
+ECDSA primitive rfc 4754 p256
+ecdsa_prim_test_vectors:SECP256R1:DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F:2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970:6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D:9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE:BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD:CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C:86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315
+
+ECDSA primitive rfc 4754 p384
+ecdsa_prim_test_vectors:SECP384R1:0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF62E528C38B2A81B35309668D73524D9F:96281BF8DD5E0525CA049C048D345D3082968D10FEDF5C5ACA0C64E6465A97EA5CE10C9DFEC21797415710721F437922:447688BA94708EB6E2E4D59F6AB6D7EDFF9301D249FE49C33096655F5D502FAD3D383B91C5E7EDAA2B714CC99D5743CA:B4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B854D7FA992F934D927376285E63414FA:CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED1631A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7:FB017B914E29149432D8BAC29A514640B46F53DDAB2C69948084E2930F1C8F7E08E07C9C63F2D21A07DCB56A6AF56EB3:B263A1305E057F984D38726A1B46874109F417BCA112674C528262A40A629AF1CBB9F516CE0FA7D2FF630863A00E8B9F
+
+ECDSA primitive rfc 4754 p521
+ecdsa_prim_test_vectors:SECP521R1:0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF5849120597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B375FA1:0151518F1AF0F563517EDD5485190DF95A4BF57B5CBA4CF2A9A3F6474725A35F7AFE0A6DDEB8BEDBCD6A197E592D40188901CECD650699C9B5E456AEA5ADD19052A8:006F3B142EA1BFFF7E2837AD44C9E4FF6D2D34C73184BBAD90026DD5E6E85317D9DF45CAD7803C6C20035B2F3FF63AFF4E1BA64D1C077577DA3F4286C58F0AEAE643:00C1C2B305419F5A41344D7E4359933D734096F556197A9B244342B8B62F46F9373778F9DE6B6497B1EF825FF24F42F9B4A4BD7382CFC3378A540B1B7F0C1B956C2F:DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA20A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD454D4423643CE80E2A9AC94FA54CA49F:0154FD3836AF92D0DCA57DD5341D3053988534FDE8318FC6AAAAB68E2E6F4339B19F2F281A7E0B22C269D93CF8794A9278880ED7DBB8D9362CAEACEE544320552251:017705A7030290D1CEB605A9A1BB03FF9CDD521E87A696EC926C8C10C8362DF4975367101F67D1CF9BCCBF2F3D239534FA509E70AAC851AE01AAC68D62F866472660
+
diff --git a/tests/suites/test_suite_ecdsa.function b/tests/suites/test_suite_ecdsa.function
new file mode 100644
index 0000000..e7d4900
--- /dev/null
+++ b/tests/suites/test_suite_ecdsa.function
@@ -0,0 +1,75 @@
+BEGIN_HEADER
+#include <polarssl/ecdsa.h>
+END_HEADER
+
+BEGIN_DEPENDENCIES
+depends_on:POLARSSL_ECDSA_C:POLARSSL_ECP_C:POLARSSL_BIGNUM_C
+END_DEPENDENCIES
+
+BEGIN_CASE
+ecdsa_prim_random:id
+{
+    ecp_group grp;
+    ecp_point Q;
+    mpi d, r, s;
+    rnd_pseudo_info rnd_info;
+    unsigned char buf[66];
+
+    ecp_group_init( &grp );
+    ecp_point_init( &Q );
+    mpi_init( &d ); mpi_init( &r ); mpi_init( &s );
+    memset( &rnd_info, 0x00, sizeof( rnd_pseudo_info ) );
+    memset( buf, 0, sizeof( buf ) );
+
+    /* prepare material for signature */
+    TEST_ASSERT( rnd_pseudo_rand( &rnd_info, buf, sizeof( buf ) ) == 0 );
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+    TEST_ASSERT( ecp_gen_keypair( &grp, &d, &Q, &rnd_pseudo_rand, &rnd_info )
+                 == 0 );
+
+    TEST_ASSERT( ecdsa_sign( &grp, &r, &s, &d, buf, sizeof( buf ),
+                             &rnd_pseudo_rand, &rnd_info ) == 0 );
+    TEST_ASSERT( ecdsa_verify( &grp, buf, sizeof( buf ), &Q, &r, &s ) == 0 );
+
+    ecp_group_free( &grp );
+    ecp_point_free( &Q );
+    mpi_free( &d ); mpi_free( &r ); mpi_free( &s );
+}
+END_CASE
+
+BEGIN_CASE
+ecdsa_prim_test_vectors:id:d:xQ:yQ:k:hash:r:s
+{
+    ecp_group grp;
+    ecp_point Q;
+    mpi d, r, s, r_check, s_check;
+    unsigned char buf[66];
+    size_t len;
+
+    ecp_group_init( &grp );
+    ecp_point_init( &Q );
+    mpi_init( &d ); mpi_init( &r ); mpi_init( &s );
+    mpi_init( &r_check ); mpi_init( &s_check );
+    memset( buf, 0, sizeof( buf ) );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+    TEST_ASSERT( ecp_point_read_string( &Q, 16, "{xQ}", "{yQ}" ) == 0 );
+    TEST_ASSERT( mpi_read_string( &d, 16, "{d}" ) == 0 );
+    TEST_ASSERT( mpi_read_string( &r_check, 16, "{r}" ) == 0 );
+    TEST_ASSERT( mpi_read_string( &s_check, 16, "{s}" ) == 0 );
+    len = unhexify(buf, "{hash}");
+
+    TEST_ASSERT( ecdsa_sign( &grp, &r, &s, &d, buf, len,
+                &not_rnd, "{k}" ) == 0 );
+
+    TEST_ASSERT( mpi_cmp_mpi( &r, &r_check ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &s, &s_check ) == 0 );
+
+    TEST_ASSERT( ecdsa_verify( &grp, buf, len, &Q, &r_check, &s_check ) == 0 );
+
+    ecp_group_free( &grp );
+    ecp_point_free( &Q );
+    mpi_free( &d ); mpi_free( &r ); mpi_free( &s );
+    mpi_free( &r_check ); mpi_free( &s_check );
+}
+END_CASE
diff --git a/tests/suites/test_suite_ecp.data b/tests/suites/test_suite_ecp.data
new file mode 100644
index 0000000..57b3113
--- /dev/null
+++ b/tests/suites/test_suite_ecp.data
@@ -0,0 +1,249 @@
+ECP small addition #1
+ecp_small_add:1:"":"":1:"":"":1:0:0
+
+ECP small addition #2
+ecp_small_add:1:"":"":0:"14":"11":0:14:11
+
+ECP small addition #3
+ecp_small_add:0:"13":"00":0:"13":"00":1:0:0
+
+ECP small addition #4
+ecp_small_add:0:"14":"11":0:"14":"36":1:0:0
+
+ECP small addition #5
+ecp_small_add:0:"13":"00":0:"37":"31":0:34:14
+
+ECP small addition #6
+ecp_small_add:0:"14":"11":0:"37":"31":0:45:07
+
+ECP small addition #7
+ecp_small_add:0:"37":"31":0:"37":"31":0:21:32
+
+ECP small addition #8
+ecp_small_add:0:"14":"11":0:"14":"11":0:27:30
+
+ECP small subtraction #1
+ecp_small_sub:1:"":"":1:"":"":1:0:0
+
+ECP small subtraction #2
+ecp_small_sub:1:"":"":0:"14":"11":0:14:36
+
+ECP small subtraction #3
+ecp_small_sub:1:"":"":0:"13":"00":0:13:00
+
+ECP small subtraction #4
+ecp_small_sub:0:"13":"00":0:"13":"00":1:0:0
+
+ECP small subtraction #5
+ecp_small_sub:0:"14":"11":0:"14":"11":1:0:0
+
+ECP small subtraction #6
+ecp_small_sub:0:"13":"00":0:"37":"16":0:34:14
+
+ECP small subtraction #7
+ecp_small_sub:0:"14":"11":0:"37":"16":0:45:07
+
+ECP small subtraction #8
+ecp_small_sub:0:"37":"31":0:"37":"16":0:21:32
+
+ECP small subtraction #9
+ecp_small_sub:0:"14":"11":0:"14":"36":0:27:30
+
+ECP small multiplication negative
+ecp_small_mul:-1:0:0:0:POLARSSL_ERR_ECP_GENERIC
+
+ECP small multiplication #0
+ecp_small_mul:0:1:0:0:0
+
+ECP small multiplication #1
+ecp_small_mul:1:0:17:42:0
+
+ECP small multiplication #2
+ecp_small_mul:2:0:20:01:0
+
+ECP small multiplication #3
+ecp_small_mul:3:0:14:11:0
+
+ECP small multiplication #4
+ecp_small_mul:4:0:34:33:0
+
+ECP small multiplication #5
+ecp_small_mul:5:0:21:32:0
+
+ECP small multiplication #6
+ecp_small_mul:6:0:27:30:0
+
+ECP small multiplication #7
+ecp_small_mul:7:0:27:17:0
+
+ECP small multiplication #8
+ecp_small_mul:8:0:21:15:0
+
+ECP small multiplication #9
+ecp_small_mul:9:0:34:14:0
+
+ECP small multiplication #10
+ecp_small_mul:10:0:14:36:0
+
+ECP small multiplication #11
+ecp_small_mul:11:0:20:46:0
+
+ECP small multiplication #12
+ecp_small_mul:12:0:17:05:0
+
+ECP small multiplication #13
+ecp_small_mul:13:1:0:0:0
+
+ECP small multiplication #14
+ecp_small_mul:1:0:17:42:0
+
+ECP small multiplication #15
+ecp_small_mul:2:0:20:01:0
+
+ECP small multiplication too big
+ecp_small_mul:-1:0:0:0:POLARSSL_ERR_ECP_GENERIC
+
+ECP small check pubkey #1
+ecp_small_check_pub:1:1:0:POLARSSL_ERR_ECP_GENERIC
+
+ECP small check pubkey #2
+ecp_small_check_pub:9:-1:1:POLARSSL_ERR_ECP_GENERIC
+
+ECP small check pubkey #3
+ecp_small_check_pub:9:46:1:0
+
+ECP small check pubkey #4
+ecp_small_check_pub:13:47:1:POLARSSL_ERR_ECP_GENERIC
+
+ECP small check pubkey #5
+ecp_small_check_pub:13:0:1:0
+
+ECP small check pubkey #6
+ecp_small_check_pub:-1:10:1:POLARSSL_ERR_ECP_GENERIC
+
+ECP small check pubkey #7
+ecp_small_check_pub:46:10:1:0
+
+ECP small check pubkey #8
+ecp_small_check_pub:47:2:1:POLARSSL_ERR_ECP_GENERIC
+
+ECP small check pubkey #9
+ecp_small_check_pub:0:2:1:0
+
+ECP small check pubkey #10
+ecp_small_check_pub:10:25:1:POLARSSL_ERR_ECP_GENERIC
+
+ECP write binary #0 (zero, bad format)
+ecp_write_binary:SECP192R1:"01":"01":"00":UNKNOWN:"00":1:POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP write binary #1 (zero, uncompressed, buffer just fits)
+ecp_write_binary:SECP192R1:"01":"01":"00":UNCOMPRESSED:"00":1:0
+
+ECP write binary #2 (zero, buffer too small)
+ecp_write_binary:SECP192R1:"01":"01":"00":UNCOMPRESSED:"00":0:POLARSSL_ERR_ECP_BUFFER_TOO_SMALL
+
+ECP write binary #3 (non-zero, uncompressed, buffer just fits)
+ecp_write_binary:SECP192R1:"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":UNCOMPRESSED:"0448d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc99336ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":49:0
+
+ECP write binary #4 (non-zero, uncompressed, buffer too small)
+ecp_write_binary:SECP192R1:"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":UNCOMPRESSED:"0448d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc99336ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":48:POLARSSL_ERR_ECP_BUFFER_TOO_SMALL
+
+ECP write binary #5 (zero, compressed, buffer just fits)
+ecp_write_binary:SECP192R1:"01":"01":"00":COMPRESSED:"00":1:0
+
+ECP write binary #6 (zero, buffer too small)
+ecp_write_binary:SECP192R1:"01":"01":"00":COMPRESSED:"00":0:POLARSSL_ERR_ECP_BUFFER_TOO_SMALL
+
+ECP write binary #7 (even, compressed, buffer just fits)
+ecp_write_binary:SECP192R1:"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":COMPRESSED:"0248d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":25:0
+
+ECP write binary #8 (even, compressed, buffer too small)
+ecp_write_binary:SECP192R1:"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":COMPRESSED:"0248d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":24:POLARSSL_ERR_ECP_BUFFER_TOO_SMALL
+
+ECP write binary #7 (odd, compressed, buffer just fits)
+ecp_write_binary:SECP192R1:"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"93112b28345b7d1d7799611e49bea9d8290cb2d7afe1f9f3":"01":COMPRESSED:"0348d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":25:0
+
+ECP read binary #1 (zero, invalid ilen)
+ecp_read_binary:SECP192R1:"0000":"01":"01":"00":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #2 (zero, invalid first byte)
+ecp_read_binary:SECP192R1:"01":"01":"01":"00":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #3 (zero, OK)
+ecp_read_binary:SECP192R1:"00":"01":"01":"00":0
+
+ECP read binary #4 (non-zero, invalid ilen)
+ecp_read_binary:SECP192R1:"04001122":"01":"01":"00":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #5 (non-zero, invalid first byte)
+ecp_read_binary:SECP192R1:"0548d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc99336ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP read binary #6 (non-zero, OK)
+ecp_read_binary:SECP192R1:"0448d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc99336ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":0
+
+ECP tls read point #1 (zero, invalid length byte)
+ecp_tls_read_point:SECP192R1:"0200":"01":"01":"00":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP tls read point #2 (zero, OK)
+ecp_tls_read_point:SECP192R1:"0100":"01":"01":"00":0
+
+ECP tls read point #3 (non-zero, invalid length byte)
+ecp_tls_read_point:SECP192R1:"300448d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc99336ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":POLARSSL_ERR_ECP_BAD_INPUT_DATA
+
+ECP tls read point #4 (non-zero, OK)
+ecp_tls_read_point:SECP192R1:"310448d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc99336ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"48d8082a3a1e3112bc03a8ef2f6d40d0a77a6f8e00cc9933":"6ceed4d7cba482e288669ee1b6415626d6f34d28501e060c":"01":0
+
+ECP tls write-read point #1
+ecp_tls_write_read_point:SECP192R1
+
+ECP tls write-read point #2
+ecp_tls_write_read_point:SECP521R1
+
+ECP tls read group #1 (record too short)
+ecp_tls_read_group:"0313":POLARSSL_ERR_ECP_BAD_INPUT_DATA:0
+
+ECP tls read group #2 (bad curve_type)
+ecp_tls_read_group:"010013":POLARSSL_ERR_ECP_BAD_INPUT_DATA:0
+
+ECP tls read group #3 (unknown curve)
+ecp_tls_read_group:"030010":POLARSSL_ERR_ECP_BAD_INPUT_DATA:0
+
+ECP tls read group #4 (OK, buffer just fits)
+ecp_tls_read_group:"030017":0:256
+
+ECP tls read group #5 (OK, buffer continues)
+ecp_tls_read_group:"0300180000":0:384
+
+ECP tls write-read group #1
+ecp_tls_write_read_group:SECP192R1
+
+ECP tls write-read group #2
+ecp_tls_write_read_group:SECP521R1
+
+ECP gen keypair
+ecp_gen_keypair:SECP192R1
+
+ECP mod p192 readable
+ecp_fast_mod:SECP192R1:"000000000000010500000000000001040000000000000103000000000000010200000000000001010000000000000100"
+
+ECP mod p192 random
+ecp_fast_mod:SECP192R1:"36CF96B45D706A0954D89E52CE5F38517A2270E0175849B6F3740151D238CCABEF921437E475881D83BB69E4AA258EBD"
+
+ECP test vectors secp192r1 rfc 5114
+ecp_test_vect:SECP192R1:"323FA3169D8E9C6593F59476BC142000AB5BE0E249C43426":"CD46489ECFD6C105E7B3D32566E2B122E249ABAADD870612":"68887B4877DF51DD4DC3D6FD11F0A26F8FD3844317916E9A":"631F95BB4A67632C9C476EEE9AB695AB240A0499307FCF62":"519A121680E0045466BA21DF2EEE47F5973B500577EF13D5":"FF613AB4D64CEE3A20875BDB10F953F6B30CA072C60AA57F":"AD420182633F8526BFE954ACDA376F05E5FF4F837F54FEBE":"4371545ED772A59741D0EDA32C671112B7FDDD51461FCF32"
+
+ECP test vectors secp224r1 rfc 5114
+ecp_test_vect:SECP224R1:"B558EB6C288DA707BBB4F8FBAE2AB9E9CB62E3BC5C7573E22E26D37F":"49DFEF309F81488C304CFF5AB3EE5A2154367DC7833150E0A51F3EEB":"4F2B5EE45762C4F654C1A0C67F54CF88B016B51BCE3D7C228D57ADB4":"AC3B1ADD3D9770E6F6A708EE9F3B8E0AB3B480E9F27F85C88B5E6D18":"6B3AC96A8D0CDE6A5599BE8032EDF10C162D0A8AD219506DCD42A207":"D491BE99C213A7D1CA3706DEBFE305F361AFCBB33E2609C8B1618AD5":"52272F50F46F4EDC9151569092F46DF2D96ECC3B6DC1714A4EA949FA":"5F30C6AA36DDC403C0ACB712BB88F1763C3046F6D919BD9C524322BF"
+
+ECP test vectors secp256r1 rfc 5114
+ecp_test_vect:SECP256R1:"814264145F2F56F2E96A8E337A1284993FAF432A5ABCE59E867B7291D507A3AF":"2AF502F3BE8952F2C9B5A8D4160D09E97165BE50BC42AE4A5E8D3B4BA83AEB15":"EB0FAF4CA986C4D38681A0F9872D79D56795BD4BFF6E6DE3C0F5015ECE5EFD85":"2CE1788EC197E096DB95A200CC0AB26A19CE6BCCAD562B8EEE1B593761CF7F41":"B120DE4AA36492795346E8DE6C2C8646AE06AAEA279FA775B3AB0715F6CE51B0":"9F1B7EECE20D7B5ED8EC685FA3F071D83727027092A8411385C34DDE5708B2B6":"DD0F5396219D1EA393310412D19A08F1F5811E9DC8EC8EEA7F80D21C820C2788":"0357DCCD4C804D0D8D33AA42B848834AA5605F9AB0D37239A115BBB647936F50"
+
+ECP test vectors secp384r1 rfc 5114
+ecp_test_vect:SECP384R1:"D27335EA71664AF244DD14E9FD1260715DFD8A7965571C48D709EE7A7962A156D706A90CBCB5DF2986F05FEADB9376F1":"793148F1787634D5DA4C6D9074417D05E057AB62F82054D10EE6B0403D6279547E6A8EA9D1FD77427D016FE27A8B8C66":"C6C41294331D23E6F480F4FB4CD40504C947392E94F4C3F06B8F398BB29E42368F7A685923DE3B67BACED214A1A1D128":"52D1791FDB4B70F89C0F00D456C2F7023B6125262C36A7DF1F80231121CCE3D39BE52E00C194A4132C4A6C768BCD94D2":"5CD42AB9C41B5347F74B8D4EFB708B3D5B36DB65915359B44ABC17647B6B9999789D72A84865AE2F223F12B5A1ABC120":"E171458FEAA939AAA3A8BFAC46B404BD8F6D5B348C0FA4D80CECA16356CA933240BDE8723415A8ECE035B0EDF36755DE":"5EA1FC4AF7256D2055981B110575E0A8CAE53160137D904C59D926EB1B8456E427AA8A4540884C37DE159A58028ABC0E":"0CC59E4B046414A81C8A3BDFDCA92526C48769DD8D3127CAA99B3632D1913942DE362EAFAA962379374D9F3F066841CA"
+
+ECP test vectors secp521r1 rfc 5114
+ecp_test_vect:SECP521R1:"0113F82DA825735E3D97276683B2B74277BAD27335EA71664AF2430CC4F33459B9669EE78B3FFB9B8683015D344DCBFEF6FB9AF4C6C470BE254516CD3C1A1FB47362":"01EBB34DD75721ABF8ADC9DBED17889CBB9765D90A7C60F2CEF007BB0F2B26E14881FD4442E689D61CB2DD046EE30E3FFD20F9A45BBDF6413D583A2DBF59924FD35C":"00F6B632D194C0388E22D8437E558C552AE195ADFD153F92D74908351B2F8C4EDA94EDB0916D1B53C020B5EECAED1A5FC38A233E4830587BB2EE3489B3B42A5A86A4":"00CEE3480D8645A17D249F2776D28BAE616952D1791FDB4B70F7C3378732AA1B22928448BCD1DC2496D435B01048066EBE4F72903C361B1A9DC1193DC2C9D0891B96":"010EBFAFC6E85E08D24BFFFCC1A4511DB0E634BEEB1B6DEC8C5939AE44766201AF6200430BA97C8AC6A0E9F08B33CE7E9FEEB5BA4EE5E0D81510C24295B8A08D0235":"00A4A6EC300DF9E257B0372B5E7ABFEF093436719A77887EBB0B18CF8099B9F4212B6E30A1419C18E029D36863CC9D448F4DBA4D2A0E60711BE572915FBD4FEF2695":"00CDEA89621CFA46B132F9E4CFE2261CDE2D4368EB5656634C7CC98C7A00CDE54ED1866A0DD3E6126C9D2F845DAFF82CEB1DA08F5D87521BB0EBECA77911169C20CC":"00F9A71641029B7FC1A808AD07CD4861E868614B865AFBECAB1F2BD4D8B55EBCB5E3A53143CEB2C511B1AE0AF5AC827F60F2FD872565AC5CA0A164038FE980A7E4BD"
+
+ECP selftest
+depends_on:POLARSSL_SELF_TEST
+ecp_selftest:
diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function
new file mode 100644
index 0000000..a051bd7
--- /dev/null
+++ b/tests/suites/test_suite_ecp.function
@@ -0,0 +1,472 @@
+BEGIN_HEADER
+#include <polarssl/ecp.h>
+END_HEADER
+
+BEGIN_DEPENDENCIES
+depends_on:POLARSSL_ECP_C:POLARSSL_BIGNUM_C
+END_DEPENDENCIES
+
+BEGIN_CASE
+ecp_small_add:a_zero:x_a:y_a:b_zero:x_b:y_b:c_zero:x_c:y_c
+{
+    ecp_group grp;
+    ecp_point A, B, C;
+
+    ecp_group_init( &grp );
+    ecp_point_init( &A ); ecp_point_init( &B ); ecp_point_init( &C );
+
+    TEST_ASSERT( ecp_group_read_string( &grp, 10,
+                "47", "4", "17", "42", "13" ) == 0 );
+
+    if( {a_zero} )
+        ecp_set_zero( &A );
+    else
+        TEST_ASSERT( ecp_point_read_string( &A, 10, {x_a}, {y_a} ) == 0 );
+
+    if( {b_zero} )
+        ecp_set_zero( &B );
+    else
+        TEST_ASSERT( ecp_point_read_string( &B, 10, {x_b}, {y_b} ) == 0 );
+
+    TEST_ASSERT( ecp_add( &grp, &C, &A, &B ) == 0 );
+
+    if( {c_zero} )
+        TEST_ASSERT( mpi_cmp_int( &C.Z, 0 ) == 0 );
+    else
+    {
+        TEST_ASSERT( mpi_cmp_int( &C.X, {x_c} ) == 0 );
+        TEST_ASSERT( mpi_cmp_int( &C.Y, {y_c} ) == 0 );
+    }
+
+    TEST_ASSERT( ecp_add( &grp, &C, &B, &A ) == 0 );
+
+    if( {c_zero} )
+        TEST_ASSERT( mpi_cmp_int( &C.Z, 0 ) == 0 );
+    else
+    {
+        TEST_ASSERT( mpi_cmp_int( &C.X, {x_c} ) == 0 );
+        TEST_ASSERT( mpi_cmp_int( &C.Y, {y_c} ) == 0 );
+    }
+
+    ecp_group_free( &grp );
+    ecp_point_free( &A ); ecp_point_free( &B ); ecp_point_free( &C );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_small_sub:a_zero:x_a:y_a:b_zero:x_b:y_b:c_zero:x_c:y_c
+{
+    ecp_group grp;
+    ecp_point A, B, C;
+
+    ecp_group_init( &grp );
+    ecp_point_init( &A ); ecp_point_init( &B ); ecp_point_init( &C );
+
+    TEST_ASSERT( ecp_group_read_string( &grp, 10,
+                "47", "4", "17", "42", "13" ) == 0 );
+
+    if( {a_zero} )
+        ecp_set_zero( &A );
+    else
+        TEST_ASSERT( ecp_point_read_string( &A, 10, {x_a}, {y_a} ) == 0 );
+
+    if( {b_zero} )
+        ecp_set_zero( &B );
+    else
+        TEST_ASSERT( ecp_point_read_string( &B, 10, {x_b}, {y_b} ) == 0 );
+
+    TEST_ASSERT( ecp_sub( &grp, &C, &A, &B ) == 0 );
+
+    if( {c_zero} )
+        TEST_ASSERT( mpi_cmp_int( &C.Z, 0 ) == 0 );
+    else
+    {
+        TEST_ASSERT( mpi_cmp_int( &C.X, {x_c} ) == 0 );
+        TEST_ASSERT( mpi_cmp_int( &C.Y, {y_c} ) == 0 );
+    }
+
+    ecp_group_free( &grp );
+    ecp_point_free( &A ); ecp_point_free( &B ); ecp_point_free( &C );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_small_mul:m:r_zero:x_r:y_r:ret
+{
+    ecp_group grp;
+    ecp_point R;
+    mpi m;
+
+    ecp_group_init( &grp );
+    ecp_point_init( &R );
+    mpi_init( &m );
+
+    TEST_ASSERT( ecp_group_read_string( &grp, 10,
+                "47", "4", "17", "42", "13" ) == 0 );
+
+    TEST_ASSERT( mpi_lset( &m, {m} ) == 0 );
+
+    TEST_ASSERT( ecp_mul( &grp, &R, &m, &grp.G ) == {ret} );
+
+    if( {r_zero} )
+        TEST_ASSERT( mpi_cmp_int( &R.Z, 0 ) == 0 );
+    else
+    {
+        TEST_ASSERT( mpi_cmp_int( &R.X, {x_r} ) == 0 );
+        TEST_ASSERT( mpi_cmp_int( &R.Y, {y_r} ) == 0 );
+    }
+
+    ecp_group_free( &grp );
+    ecp_point_free( &R );
+    mpi_free( &m );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_small_check_pub:x:y:z:ret
+{
+    ecp_group grp;
+    ecp_point P;
+
+    ecp_group_init( &grp );
+    ecp_point_init( &P );
+
+    TEST_ASSERT( ecp_group_read_string( &grp, 10,
+                "47", "4", "17", "42", "13" ) == 0 );
+
+    TEST_ASSERT( mpi_lset( &P.X, {x} ) == 0 );
+    TEST_ASSERT( mpi_lset( &P.Y, {y} ) == 0 );
+    TEST_ASSERT( mpi_lset( &P.Z, {z} ) == 0 );
+
+    TEST_ASSERT( ecp_check_pubkey( &grp, &P ) == {ret} );
+
+    ecp_group_free( &grp );
+    ecp_point_free( &P );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_test_vect:id:dA:xA:yA:dB:xB:yB:xZ:yZ
+{
+    ecp_group grp;
+    ecp_point R;
+    mpi dA, xA, yA, dB, xB, yB, xZ, yZ;
+
+    ecp_group_init( &grp ); ecp_point_init( &R );
+    mpi_init( &dA ); mpi_init( &xA ); mpi_init( &yA ); mpi_init( &dB );
+    mpi_init( &xB ); mpi_init( &yB ); mpi_init( &xZ ); mpi_init( &yZ );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    TEST_ASSERT( ecp_check_pubkey( &grp, &grp.G ) == 0 );
+
+    TEST_ASSERT( mpi_read_string( &dA, 16, {dA} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &xA, 16, {xA} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &yA, 16, {yA} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &dB, 16, {dB} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &xB, 16, {xB} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &yB, 16, {yB} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &xZ, 16, {xZ} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &yZ, 16, {yZ} ) == 0 );
+
+    TEST_ASSERT( ecp_mul( &grp, &R, &dA, &grp.G ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &R.X, &xA ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &R.Y, &yA ) == 0 );
+    TEST_ASSERT( ecp_check_pubkey( &grp, &R ) == 0 );
+    TEST_ASSERT( ecp_mul( &grp, &R, &dB, &R ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &R.X, &xZ ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &R.Y, &yZ ) == 0 );
+    TEST_ASSERT( ecp_check_pubkey( &grp, &R ) == 0 );
+
+    TEST_ASSERT( ecp_mul( &grp, &R, &dB, &grp.G ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &R.X, &xB ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &R.Y, &yB ) == 0 );
+    TEST_ASSERT( ecp_check_pubkey( &grp, &R ) == 0 );
+    TEST_ASSERT( ecp_mul( &grp, &R, &dA, &R ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &R.X, &xZ ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &R.Y, &yZ ) == 0 );
+    TEST_ASSERT( ecp_check_pubkey( &grp, &R ) == 0 );
+
+    ecp_group_free( &grp ); ecp_point_free( &R );
+    mpi_free( &dA ); mpi_free( &xA ); mpi_free( &yA ); mpi_free( &dB );
+    mpi_free( &xB ); mpi_free( &yB ); mpi_free( &xZ ); mpi_free( &yZ );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_fast_mod:id:N
+{
+    ecp_group grp;
+    mpi N, R;
+
+    mpi_init( &N ); mpi_init( &R );
+    ecp_group_init( &grp );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &N, 16, {N} ) == 0 );
+
+    /*
+     * Store correct result before we touch N
+     */
+    TEST_ASSERT( mpi_mod_mpi( &R, &N, &grp.P ) == 0 );
+
+    TEST_ASSERT( grp.modp( &N ) == 0 );
+    TEST_ASSERT( mpi_msb( &N ) <= grp.pbits + 3 );
+
+    /*
+     * Use mod rather than addition/substraction in case previous test fails
+     */
+    TEST_ASSERT( mpi_mod_mpi( &N, &N, &grp.P ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &N, &R ) == 0 );
+
+    mpi_free( &N ); mpi_free( &R );
+    ecp_group_free( &grp );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_write_binary:id:x:y:z:format:out:blen:ret
+{
+    ecp_group grp;
+    ecp_point P;
+    unsigned char buf[256], str[512];
+    size_t olen;
+
+    memset( buf, 0, sizeof( buf ) );
+    memset( str, 0, sizeof( str ) );
+
+    ecp_group_init( &grp ); ecp_point_init( &P );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    TEST_ASSERT( mpi_read_string( &P.X, 16, {x} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &P.Y, 16, {y} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &P.Z, 16, {z} ) == 0 );
+
+#define POLARSSL_ECP_PF_UNKNOWN     -1
+    TEST_ASSERT( ecp_point_write_binary( &grp, &P, POLARSSL_ECP_PF_{format},
+                                   &olen, buf, {blen} ) == {ret} );
+
+    if( {ret} == 0 )
+    {
+        hexify( str, buf, olen );
+        TEST_ASSERT( strcasecmp( (char *) str, {out} ) == 0 );
+    }
+
+    ecp_group_free( &grp ); ecp_point_free( &P );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_read_binary:id:input:x:y:z:ret
+{
+    ecp_group grp;
+    ecp_point P;
+    mpi X, Y, Z;
+    int ilen;
+    unsigned char buf[256];
+
+    memset( buf, 0, sizeof( buf ) );
+
+    ecp_group_init( &grp ); ecp_point_init( &P );
+    mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    TEST_ASSERT( mpi_read_string( &X, 16, {x} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &Y, 16, {y} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &Z, 16, {z} ) == 0 );
+
+    ilen = unhexify( buf, {input} );
+
+    TEST_ASSERT( ecp_point_read_binary( &grp, &P, buf, ilen ) == {ret} );
+
+    if( {ret} == 0 )
+    {
+        TEST_ASSERT( mpi_cmp_mpi( &P.X, &X ) == 0 );
+        TEST_ASSERT( mpi_cmp_mpi( &P.Y, &Y ) == 0 );
+        TEST_ASSERT( mpi_cmp_mpi( &P.Z, &Z ) == 0 );
+    }
+
+    ecp_group_free( &grp ); ecp_point_free( &P );
+    mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_tls_read_point:id:input:x:y:z:ret
+{
+    ecp_group grp;
+    ecp_point P;
+    mpi X, Y, Z;
+    size_t ilen;
+    unsigned char buf[256];
+    const unsigned char *vbuf = buf;
+
+    memset( buf, 0, sizeof( buf ) );
+
+    ecp_group_init( &grp ); ecp_point_init( &P );
+    mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    TEST_ASSERT( mpi_read_string( &X, 16, {x} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &Y, 16, {y} ) == 0 );
+    TEST_ASSERT( mpi_read_string( &Z, 16, {z} ) == 0 );
+
+    ilen = unhexify( buf, {input} );
+
+    TEST_ASSERT( ecp_tls_read_point( &grp, &P, &vbuf, ilen ) == {ret} );
+
+    if( {ret} == 0 )
+    {
+        TEST_ASSERT( mpi_cmp_mpi( &P.X, &X ) == 0 );
+        TEST_ASSERT( mpi_cmp_mpi( &P.Y, &Y ) == 0 );
+        TEST_ASSERT( mpi_cmp_mpi( &P.Z, &Z ) == 0 );
+        TEST_ASSERT( *vbuf == 0x00 );
+    }
+
+    ecp_group_free( &grp ); ecp_point_free( &P );
+    mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_tls_write_read_point:id
+{
+    ecp_group grp;
+    ecp_point pt;
+    unsigned char buf[256];
+    const unsigned char *vbuf;
+    size_t olen;
+
+    ecp_group_init( &grp );
+    ecp_point_init( &pt );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    memset( buf, 0x00, sizeof( buf ) ); vbuf = buf;
+    TEST_ASSERT( ecp_tls_write_point( &grp, &grp.G,
+                    POLARSSL_ECP_PF_COMPRESSED, &olen, buf, 256 ) == 0 );
+    TEST_ASSERT( ecp_tls_read_point( &grp, &pt, &vbuf, olen )
+                 == POLARSSL_ERR_ECP_BAD_INPUT_DATA );
+    TEST_ASSERT( vbuf == buf + olen );
+
+    memset( buf, 0x00, sizeof( buf ) ); vbuf = buf;
+    TEST_ASSERT( ecp_tls_write_point( &grp, &grp.G,
+                    POLARSSL_ECP_PF_UNCOMPRESSED, &olen, buf, 256 ) == 0 );
+    TEST_ASSERT( ecp_tls_read_point( &grp, &pt, &vbuf, olen ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &grp.G.X, &pt.X ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &grp.G.Y, &pt.Y ) == 0 );
+    TEST_ASSERT( mpi_cmp_mpi( &grp.G.Z, &pt.Z ) == 0 );
+    TEST_ASSERT( vbuf == buf + olen );
+
+    memset( buf, 0x00, sizeof( buf ) ); vbuf = buf;
+    TEST_ASSERT( ecp_set_zero( &pt ) == 0 );
+    TEST_ASSERT( ecp_tls_write_point( &grp, &pt,
+                    POLARSSL_ECP_PF_COMPRESSED, &olen, buf, 256 ) == 0 );
+    TEST_ASSERT( ecp_tls_read_point( &grp, &pt, &vbuf, olen ) == 0 );
+    TEST_ASSERT( ecp_is_zero( &pt ) );
+    TEST_ASSERT( vbuf == buf + olen );
+
+    memset( buf, 0x00, sizeof( buf ) ); vbuf = buf;
+    TEST_ASSERT( ecp_set_zero( &pt ) == 0 );
+    TEST_ASSERT( ecp_tls_write_point( &grp, &pt,
+                    POLARSSL_ECP_PF_UNCOMPRESSED, &olen, buf, 256 ) == 0 );
+    TEST_ASSERT( ecp_tls_read_point( &grp, &pt, &vbuf, olen ) == 0 );
+    TEST_ASSERT( ecp_is_zero( &pt ) );
+    TEST_ASSERT( vbuf == buf + olen );
+
+    ecp_group_free( &grp );
+    ecp_point_free( &pt );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_tls_read_group:record:ret:bits
+{
+    ecp_group grp;
+    unsigned char buf[10];
+    const unsigned char *vbuf = buf;
+    int len, ret;
+
+    ecp_group_init( &grp );
+    memset( buf, 0x00, sizeof( buf ) );
+
+    len = unhexify( buf, {record} );
+
+    ret = ecp_tls_read_group( &grp, &vbuf, len );
+
+    TEST_ASSERT( ret == {ret} );
+    if( ret == 0)
+    {
+        TEST_ASSERT( mpi_msb( &grp.P ) == {bits} );
+        TEST_ASSERT( *vbuf == 0x00 );
+    }
+
+    ecp_group_free( &grp );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_tls_write_read_group:id
+{
+    ecp_group grp1, grp2;
+    unsigned char buf[10];
+    const unsigned char *vbuf = buf;
+    size_t len;
+    int ret;
+
+    ecp_group_init( &grp1 );
+    ecp_group_init( &grp2 );
+    memset( buf, 0x00, sizeof( buf ) );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp1, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    TEST_ASSERT( ecp_tls_write_group( &grp1, &len, buf, 10 ) == 0 );
+    TEST_ASSERT( ( ret = ecp_tls_read_group( &grp2, &vbuf, len ) ) == 0 );
+
+    if( ret == 0 )
+    {
+        TEST_ASSERT( mpi_cmp_mpi( &grp1.N, &grp2.N ) == 0 );
+        TEST_ASSERT( grp1.id == grp2.id );
+    }
+
+    ecp_group_free( &grp1 );
+    ecp_group_free( &grp2 );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_gen_keypair:id
+{
+    ecp_group grp;
+    ecp_point Q;
+    mpi d;
+    rnd_pseudo_info rnd_info;
+
+    ecp_group_init( &grp );
+    ecp_point_init( &Q );
+    mpi_init( &d );
+    memset( &rnd_info, 0x00, sizeof( rnd_pseudo_info ) );
+
+    TEST_ASSERT( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_{id} ) == 0 );
+
+    TEST_ASSERT( ecp_gen_keypair( &grp, &d, &Q, &rnd_pseudo_rand, &rnd_info )
+                 == 0 );
+
+    TEST_ASSERT( mpi_cmp_mpi( &d, &grp.N ) < 0 );
+    TEST_ASSERT( mpi_cmp_int( &d, 1 ) >= 0 );
+
+    ecp_group_free( &grp );
+    ecp_point_free( &Q );
+    mpi_free( &d );
+}
+END_CASE
+
+BEGIN_CASE
+ecp_selftest:
+{
+    TEST_ASSERT( ecp_self_test( 0 ) == 0 );
+}
+END_CASE