Merge pull request #328 from ARMmbed/iotssl-461-ecjpake-finalization

Iotssl 461 ecjpake finalization
diff --git a/ChangeLog b/ChangeLog
index aa96b18..b8a24fc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 mbed TLS ChangeLog (Sorted per branch, date)
 
+= mbed TLS 2.2.0 released 2015-10-xx
+
+Features
+   * Experimental support for EC J-PAKE as defined in Thread 1.0.0.
+     Disabled by default as the specification might still change.
+   * Added a key extraction callback to accees the master secret and key
+     block. (Potential uses include EAP-TLS and Thread.)
+
 = mbed TLS 2.1.2 released 2015-10-06
 
 Security
diff --git a/configs/config-thread.h b/configs/config-thread.h
new file mode 100644
index 0000000..453b17f
--- /dev/null
+++ b/configs/config-thread.h
@@ -0,0 +1,94 @@
+/*
+ *  Minimal configuration for using TLS as part of Thread
+ *
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+/*
+ * Minimal configuration for using TLS a part of Thread
+ * http://threadgroup.org/
+ *
+ * Distinguishing features:
+ * - no RSA or classic DH, fully based on ECC
+ * - no X.509
+ * - support for experimental EC J-PAKE key exchange
+ *
+ * See README.txt for usage instructions.
+ */
+
+#ifndef MBEDTLS_CONFIG_H
+#define MBEDTLS_CONFIG_H
+
+/* System support */
+#define MBEDTLS_HAVE_ASM
+
+/* mbed TLS feature support */
+#define MBEDTLS_AES_ROM_TABLES
+#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
+#define MBEDTLS_ECP_NIST_OPTIM
+#define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
+#define MBEDTLS_SSL_PROTO_TLS1_2
+#define MBEDTLS_SSL_PROTO_DTLS
+#define MBEDTLS_SSL_DTLS_ANTI_REPLAY
+#define MBEDTLS_SSL_DTLS_HELLO_VERIFY
+#define MBEDTLS_SSL_EXPORT_KEYS
+
+/* mbed TLS modules */
+#define MBEDTLS_AES_C
+#define MBEDTLS_ASN1_PARSE_C
+#define MBEDTLS_ASN1_WRITE_C
+#define MBEDTLS_BIGNUM_C
+#define MBEDTLS_CCM_C
+#define MBEDTLS_CIPHER_C
+#define MBEDTLS_CTR_DRBG_C
+#define MBEDTLS_ECJPAKE_C
+#define MBEDTLS_ECP_C
+#define MBEDTLS_ENTROPY_C
+#define MBEDTLS_HMAC_DRBG_C
+#define MBEDTLS_MD_C
+#define MBEDTLS_OID_C
+#define MBEDTLS_PK_C
+#define MBEDTLS_PK_PARSE_C
+#define MBEDTLS_SHA256_C
+#define MBEDTLS_SSL_COOKIE_C
+#define MBEDTLS_SSL_CLI_C
+#define MBEDTLS_SSL_SRV_C
+#define MBEDTLS_SSL_TLS_C
+
+/* For tests using ssl-opt.sh */
+#define MBEDTLS_NET_C
+#define MBEDTLS_TIMING_C
+
+/* Save RAM at the expense of ROM */
+#define MBEDTLS_AES_ROM_TABLES
+
+/* Save RAM by adjusting to our exact needs */
+#define MBEDTLS_ECP_MAX_BITS             256
+#define MBEDTLS_MPI_MAX_SIZE              32 // 256 bits is 32 bytes
+
+/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */
+#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8
+
+#if defined(TARGET_LIKE_MBED)
+#include "mbedtls/target_config.h"
+#endif
+
+#include "mbedtls/check_config.h"
+
+#endif /* MBEDTLS_CONFIG_H */
diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
index 8dadbe1..b6448ec 100644
--- a/include/mbedtls/check_config.h
+++ b/include/mbedtls/check_config.h
@@ -88,6 +88,11 @@
 #error "MBEDTLS_ECDSA_C defined, but not all prerequisites"
 #endif
 
+#if defined(MBEDTLS_ECJPAKE_C) &&           \
+    ( !defined(MBEDTLS_ECP_C) || !defined(MBEDTLS_MD_C) )
+#error "MBEDTLS_ECJPAKE_C defined, but not all prerequisites"
+#endif
+
 #if defined(MBEDTLS_ECDSA_DETERMINISTIC) && !defined(MBEDTLS_HMAC_DRBG_C)
 #error "MBEDTLS_ECDSA_DETERMINISTIC defined, but not all prerequisites"
 #endif
@@ -187,6 +192,12 @@
 #error "MBEDTLS_KEY_EXCHANGE_RSA_ENABLED defined, but not all prerequisites"
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) &&                    \
+    ( !defined(MBEDTLS_ECJPAKE_C) || !defined(MBEDTLS_SHA256_C) ||      \
+      !defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) )
+#error "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED defined, but not all prerequisites"
+#endif
+
 #if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) &&                          \
     ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_PLATFORM_MEMORY) )
 #error "MBEDTLS_MEMORY_BUFFER_ALLOC_C defined, but not all prerequisites"
diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
index 68153ef..3e39998 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -695,6 +695,25 @@
 #define MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED
 
 /**
+ * \def MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+ *
+ * Enable the ECJPAKE based ciphersuite modes in SSL / TLS.
+ *
+ * \warning This is currently experimental. EC J-PAKE support is based on the
+ * Thread v1.0.0 specification; incompatible changes to the specification
+ * might still happen. For this reason, this is disabled by default.
+ *
+ * Requires: MBEDTLS_ECJPAKE_C
+ *           MBEDTLS_SHA256_C
+ *           MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ *
+ * This enables the following ciphersuites (if other requisites are
+ * enabled as well):
+ *      MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8
+ */
+//#define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+
+/**
  * \def MBEDTLS_PK_PARSE_EC_EXTENDED
  *
  * Enhance support for reading EC keys using variants of SEC1 not allowed by
@@ -1176,6 +1195,16 @@
 #define MBEDTLS_SSL_SESSION_TICKETS
 
 /**
+ * \def MBEDTLS_SSL_EXPORT_KEYS
+ *
+ * Enable support for exporting key block and master secret.
+ * This is required for certain users of TLS, e.g. EAP-TLS.
+ *
+ * Comment this macro to disable support for key export
+ */
+#define MBEDTLS_SSL_EXPORT_KEYS
+
+/**
  * \def MBEDTLS_SSL_SERVER_NAME_INDICATION
  *
  * Enable support for RFC 6066 server name indication (SNI) in SSL.
@@ -1689,6 +1718,25 @@
 #define MBEDTLS_ECDSA_C
 
 /**
+ * \def MBEDTLS_ECJPAKE_C
+ *
+ * Enable the elliptic curve J-PAKE library.
+ *
+ * \warning This is currently experimental. EC J-PAKE support is based on the
+ * Thread v1.0.0 specification; incompatible changes to the specification
+ * might still happen. For this reason, this is disabled by default.
+ *
+ * Module:  library/ecjpake.c
+ * Caller:
+ *
+ * This module is used by the following key exchanges:
+ *      ECJPAKE
+ *
+ * Requires: MBEDTLS_ECP_C, MBEDTLS_MD_C
+ */
+//#define MBEDTLS_ECJPAKE_C
+
+/**
  * \def MBEDTLS_ECP_C
  *
  * Enable the elliptic curve over GF(p) library.
@@ -1696,6 +1744,7 @@
  * Module:  library/ecp.c
  * Caller:  library/ecdh.c
  *          library/ecdsa.c
+ *          library/ecjpake.c
  *
  * Requires: MBEDTLS_BIGNUM_C and at least one MBEDTLS_ECP_DP_XXX_ENABLED
  */
diff --git a/include/mbedtls/ecjpake.h b/include/mbedtls/ecjpake.h
new file mode 100644
index 0000000..3bbf27e
--- /dev/null
+++ b/include/mbedtls/ecjpake.h
@@ -0,0 +1,238 @@
+/**
+ * \file ecjpake.h
+ *
+ * \brief Elliptic curve J-PAKE
+ *
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+#ifndef MBEDTLS_ECJPAKE_H
+#define MBEDTLS_ECJPAKE_H
+
+/*
+ * J-PAKE is a password-authenticated key exchange that allows deriving a
+ * strong shared secret from a (potentially low entropy) pre-shared
+ * passphrase, with forward secrecy and mutual authentication.
+ * https://en.wikipedia.org/wiki/Password_Authenticated_Key_Exchange_by_Juggling
+ *
+ * This file implements the Elliptic Curve variant of J-PAKE,
+ * as defined in Chapter 7.4 of the Thread v1.0 Specification,
+ * available to members of the Thread Group http://threadgroup.org/
+ *
+ * As the J-PAKE algorithm is inherently symmetric, so is our API.
+ * Each party needs to send its first round message, in any order, to the
+ * other party, then each sends its second round message, in any order.
+ * The payloads are serialized in a way suitable for use in TLS, but could
+ * also be use outside TLS.
+ */
+
+#include "ecp.h"
+#include "md.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Roles in the EC J-PAKE exchange
+ */
+typedef enum {
+    MBEDTLS_ECJPAKE_CLIENT = 0,         /**< Client                         */
+    MBEDTLS_ECJPAKE_SERVER,             /**< Server                         */
+} mbedtls_ecjpake_role;
+
+/**
+ * EC J-PAKE context structure.
+ *
+ * J-PAKE is a symmetric protocol, except for the identifiers used in
+ * Zero-Knowledge Proofs, and the serialization of the second message
+ * (KeyExchange) as defined by the Thread spec.
+ *
+ * In order to benefit from this symmetry, we choose a different naming
+ * convetion from the Thread v1.0 spec. Correspondance is indicated in the
+ * description as a pair C: <client name>, S: <server name>
+ */
+typedef struct
+{
+    const mbedtls_md_info_t *md_info;   /**< Hash to use                    */
+    mbedtls_ecp_group grp;              /**< Elliptic curve                 */
+    mbedtls_ecjpake_role role;          /**< Are we client or server?       */
+    int point_format;                   /**< Format for point export        */
+
+    mbedtls_ecp_point Xm1;              /**< My public key 1   C: X1, S: X3 */
+    mbedtls_ecp_point Xm2;              /**< My public key 2   C: X2, S: X4 */
+    mbedtls_ecp_point Xp1;              /**< Peer public key 1 C: X3, S: X1 */
+    mbedtls_ecp_point Xp2;              /**< Peer public key 2 C: X4, S: X2 */
+    mbedtls_ecp_point Xp;               /**< Peer public key   C: Xs, S: Xc */
+
+    mbedtls_mpi xm1;                    /**< My private key 1  C: x1, S: x3 */
+    mbedtls_mpi xm2;                    /**< My private key 2  C: x2, S: x4 */
+
+    mbedtls_mpi s;                      /**< Pre-shared secret (passphrase) */
+} mbedtls_ecjpake_context;
+
+/**
+ * \brief           Initialize a context
+ *                  (just makes it ready for setup() or free()).
+ *
+ * \param ctx       context to initialize
+ */
+void mbedtls_ecjpake_init( mbedtls_ecjpake_context *ctx );
+
+/**
+ * \brief           Set up a context for use
+ *
+ * \note            Currently the only values for hash/curve allowed by the
+ *                  standard are MBEDTLS_MD_SHA256/MBEDTLS_ECP_DP_SECP256R1.
+ *
+ * \param ctx       context to set up
+ * \param role      Our role: client or server
+ * \param hash      hash function to use (MBEDTLS_MD_XXX)
+ * \param curve     elliptic curve identifier (MBEDTLS_ECP_DP_XXX)
+ * \param secret    pre-shared secret (passphrase)
+ * \param len       length of the shared secret
+ *
+ * \return          0 if successfull,
+ *                  a negative error code otherwise
+ */
+int mbedtls_ecjpake_setup( mbedtls_ecjpake_context *ctx,
+                           mbedtls_ecjpake_role role,
+                           mbedtls_md_type_t hash,
+                           mbedtls_ecp_group_id curve,
+                           const unsigned char *secret,
+                           size_t len );
+
+/*
+ * \brief           Check if a context is ready for use
+ *
+ * \param ctx       Context to check
+ *
+ * \return          0 if the context is ready for use,
+ *                  MBEDTLS_ERR_ECP_BAD_INPUT_DATA otherwise
+ */
+int mbedtls_ecjpake_check( const mbedtls_ecjpake_context *ctx );
+
+/**
+ * \brief           Generate and write the first round message
+ *                  (TLS: contents of the Client/ServerHello extension,
+ *                  excluding extension type and length bytes)
+ *
+ * \param ctx       Context to use
+ * \param buf       Buffer to write the contents to
+ * \param len       Buffer size
+ * \param olen      Will be updated with the number of bytes written
+ * \param f_rng     RNG function
+ * \param p_rng     RNG parameter
+ *
+ * \return          0 if successfull,
+ *                  a negative error code otherwise
+ */
+int mbedtls_ecjpake_write_round_one( mbedtls_ecjpake_context *ctx,
+                            unsigned char *buf, size_t len, size_t *olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng );
+
+/**
+ * \brief           Read and process the first round message
+ *                  (TLS: contents of the Client/ServerHello extension,
+ *                  excluding extension type and length bytes)
+ *
+ * \param ctx       Context to use
+ * \param buf       Pointer to extension contents
+ * \param len       Extension length
+ *
+ * \return          0 if successfull,
+ *                  a negative error code otherwise
+ */
+int mbedtls_ecjpake_read_round_one( mbedtls_ecjpake_context *ctx,
+                                    const unsigned char *buf,
+                                    size_t len );
+
+/**
+ * \brief           Generate and write the second round message
+ *                  (TLS: contents of the Client/ServerKeyExchange)
+ *
+ * \param ctx       Context to use
+ * \param buf       Buffer to write the contents to
+ * \param len       Buffer size
+ * \param olen      Will be updated with the number of bytes written
+ * \param f_rng     RNG function
+ * \param p_rng     RNG parameter
+ *
+ * \return          0 if successfull,
+ *                  a negative error code otherwise
+ */
+int mbedtls_ecjpake_write_round_two( mbedtls_ecjpake_context *ctx,
+                            unsigned char *buf, size_t len, size_t *olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng );
+
+/**
+ * \brief           Read and process the second round message
+ *                  (TLS: contents of the Client/ServerKeyExchange)
+ *
+ * \param ctx       Context to use
+ * \param buf       Pointer to the message
+ * \param len       Message length
+ *
+ * \return          0 if successfull,
+ *                  a negative error code otherwise
+ */
+int mbedtls_ecjpake_read_round_two( mbedtls_ecjpake_context *ctx,
+                                    const unsigned char *buf,
+                                    size_t len );
+
+/**
+ * \brief           Derive the shared secret
+ *                  (TLS: Pre-Master Secret)
+ *
+ * \param ctx       Context to use
+ * \param buf       Buffer to write the contents to
+ * \param len       Buffer size
+ * \param olen      Will be updated with the number of bytes written
+ * \param f_rng     RNG function
+ * \param p_rng     RNG parameter
+ *
+ * \return          0 if successfull,
+ *                  a negative error code otherwise
+ */
+int mbedtls_ecjpake_derive_secret( mbedtls_ecjpake_context *ctx,
+                            unsigned char *buf, size_t len, size_t *olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng );
+
+/**
+ * \brief           Free a context's content
+ *
+ * \param ctx       context to free
+ */
+void mbedtls_ecjpake_free( mbedtls_ecjpake_context *ctx );
+
+#if defined(MBEDTLS_SELF_TEST)
+/**
+ * \brief          Checkup routine
+ *
+ * \return         0 if successful, or 1 if a test failed
+ */
+int mbedtls_ecjpake_self_test( int verbose );
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ecjpake.h */
diff --git a/include/mbedtls/ecp.h b/include/mbedtls/ecp.h
index 723e46c..5246c78 100644
--- a/include/mbedtls/ecp.h
+++ b/include/mbedtls/ecp.h
@@ -347,6 +347,21 @@
 int mbedtls_ecp_is_zero( mbedtls_ecp_point *pt );
 
 /**
+ * \brief           Compare two points
+ *
+ * \note            This assumes the points are normalized. Otherwise,
+ *                  they may compare as "not equal" even if they are.
+ *
+ * \param P         First point to compare
+ * \param Q         Second point to compare
+ *
+ * \return          0 if the points are equal,
+ *                  MBEDTLS_ERR_ECP_BAD_INPUT_DATA otherwise
+ */
+int mbedtls_ecp_point_cmp( const mbedtls_ecp_point *P,
+                           const mbedtls_ecp_point *Q );
+
+/**
  * \brief           Import a non-zero point from two ASCII strings
  *
  * \param P         Destination point
@@ -570,6 +585,29 @@
 int mbedtls_ecp_check_privkey( const mbedtls_ecp_group *grp, const mbedtls_mpi *d );
 
 /**
+ * \brief           Generate a keypair with configurable base point
+ *
+ * \param grp       ECP group
+ * \param G         Chosen base point
+ * \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 MBEDTLS_ERR_ECP_XXX or MBEDTLS_MPI_XXX error code
+ *
+ * \note            Uses bare components rather than an mbedtls_ecp_keypair structure
+ *                  in order to ease use with other structures such as
+ *                  mbedtls_ecdh_context of mbedtls_ecdsa_context.
+ */
+int mbedtls_ecp_gen_keypair_base( mbedtls_ecp_group *grp,
+                     const mbedtls_ecp_point *G,
+                     mbedtls_mpi *d, mbedtls_ecp_point *Q,
+                     int (*f_rng)(void *, unsigned char *, size_t),
+                     void *p_rng );
+
+/**
  * \brief           Generate a keypair
  *
  * \param grp       ECP group
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index a017ec0..4c06199 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -352,6 +352,8 @@
 
 #define MBEDTLS_TLS_EXT_SESSION_TICKET              35
 
+#define MBEDTLS_TLS_EXT_ECJPAKE_KKPP               256 /* experimental */
+
 #define MBEDTLS_TLS_EXT_RENEGOTIATION_INFO      0xFF01
 
 /*
@@ -390,6 +392,9 @@
     unsigned char _pms_ecdhe_psk[4 + MBEDTLS_ECP_MAX_BYTES
                                    + MBEDTLS_PSK_MAX_LEN];     /* RFC 5489 2 */
 #endif
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    unsigned char _pms_ecjpake[32];     /* Thread spec: SHA-256 output */
+#endif
 };
 
 #define MBEDTLS_PREMASTER_SIZE     sizeof( union mbedtls_ssl_premaster_secret )
@@ -542,6 +547,13 @@
     void *p_ticket;                 /*!< context for the ticket callbacks   */
 #endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_SRV_C */
 
+#if defined(MBEDTLS_SSL_EXPORT_KEYS)
+    /** Callback to export key block and master secret                      */
+    int (*f_export_keys)( void *, const unsigned char *,
+            const unsigned char *, size_t, size_t, size_t );
+    void *p_export_keys;            /*!< context for key export callback    */
+#endif
+
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
     const mbedtls_x509_crt_profile *cert_profile; /*!< verification profile */
     mbedtls_ssl_key_cert *key_cert; /*!< own certificate/key pair(s)        */
@@ -1069,6 +1081,35 @@
                                         size_t *tlen,
                                         uint32_t *lifetime );
 
+#if defined(MBEDTLS_SSL_EXPORT_KEYS)
+/**
+ * \brief           Callback type: Export key block and master secret
+ *
+ * \note            This is required for certain uses of TLS, e.g. EAP-TLS
+ *                  (RFC 5216) and Thread. The key pointers are ephemeral and
+ *                  therefore must not be stored. The master secret and keys
+ *                  should not be used directly except as an input to a key
+ *                  derivation function.
+ *
+ * \param p_expkey  Context for the callback
+ * \param ms        Pointer to master secret (fixed length: 48 bytes)
+ * \param kb        Pointer to key block, see RFC 5246 section 6.3
+ *                  (variable length: 2 * maclen + 2 * keylen + 2 * ivlen).
+ * \param maclen    MAC length
+ * \param keylen    Key length
+ * \param ivlen     IV length
+ *
+ * \return          0 if successful, or
+ *                  a specific MBEDTLS_ERR_XXX code.
+ */
+typedef int mbedtls_ssl_export_keys_t( void *p_expkey,
+                                const unsigned char *ms,
+                                const unsigned char *kb,
+                                size_t maclen,
+                                size_t keylen,
+                                size_t ivlen );
+#endif /* MBEDTLS_SSL_EXPORT_KEYS */
+
 /**
  * \brief           Callback type: parse and load session ticket
  *
@@ -1118,6 +1159,22 @@
         void *p_ticket );
 #endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_SRV_C */
 
+#if defined(MBEDTLS_SSL_EXPORT_KEYS)
+/**
+ * \brief           Configure key export callback.
+ *                  (Default: none.)
+ *
+ * \note            See \c mbedtls_ssl_export_keys_t.
+ *
+ * \param conf      SSL configuration context
+ * \param f_export_keys     Callback for exporting keys
+ * \param p_export_keys     Context for the callback
+ */
+void mbedtls_ssl_conf_export_keys_cb( mbedtls_ssl_config *conf,
+        mbedtls_ssl_export_keys_t *f_export_keys,
+        void *p_export_keys );
+#endif /* MBEDTLS_SSL_EXPORT_KEYS */
+
 /**
  * \brief          Callback type: generate a cookie
  *
@@ -1679,6 +1736,29 @@
                   void *p_sni );
 #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+/**
+ * \brief          Set the EC J-PAKE password for current handshake.
+ *
+ * \note           An internal copy is made, and destroyed as soon as the
+ *                 handshake is completed, or when the SSL context is reset or
+ *                 freed.
+ *
+ * \note           The SSL context needs to be already set up. The right place
+ *                 to call this function is between \c mbedtls_ssl_setup() or
+ *                 \c mbedtls_ssl_reset() and \c mbedtls_ssl_handshake().
+ *
+ * \param ssl      SSL context
+ * \param pw       EC J-PAKE password (pre-shared secret)
+ * \param pw_len   length of pw in bytes
+ *
+ * \return         0 on success, or a negative error code.
+ */
+int mbedtls_ssl_set_hs_ecjpake_password( mbedtls_ssl_context *ssl,
+                                         const unsigned char *pw,
+                                         size_t pw_len );
+#endif /*MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
 #if defined(MBEDTLS_SSL_ALPN)
 /**
  * \brief          Set the supported Application Layer Protocols.
diff --git a/include/mbedtls/ssl_ciphersuites.h b/include/mbedtls/ssl_ciphersuites.h
index 75d4a8a..9cf3f42 100644
--- a/include/mbedtls/ssl_ciphersuites.h
+++ b/include/mbedtls/ssl_ciphersuites.h
@@ -229,6 +229,8 @@
 #define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8      0xC0AE  /**< TLS 1.2 */
 #define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8      0xC0AF  /**< TLS 1.2 */
 
+#define MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8          0xC0FF  /**< experimental */
+
 /* Reminder: update mbedtls_ssl_premaster_secret when adding a new key exchange.
  * Reminder: update MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED below.
  */
@@ -244,6 +246,7 @@
     MBEDTLS_KEY_EXCHANGE_ECDHE_PSK,
     MBEDTLS_KEY_EXCHANGE_ECDH_RSA,
     MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA,
+    MBEDTLS_KEY_EXCHANGE_ECJPAKE,
 } mbedtls_key_exchange_type_t;
 
 #if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED)          || \
diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h
index 78c748c..ecfc971 100644
--- a/include/mbedtls/ssl_internal.h
+++ b/include/mbedtls/ssl_internal.h
@@ -41,6 +41,10 @@
 #include "sha512.h"
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+#include "ecjpake.h"
+#endif
+
 #if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
     !defined(inline) && !defined(__cplusplus)
 #define inline __inline
@@ -147,6 +151,7 @@
  * of state of the renegotiation flag, so no indicator is required)
  */
 #define MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS_PRESENT (1 << 0)
+#define MBEDTLS_TLS_EXT_ECJPAKE_KKPP_OK                 (1 << 1)
 
 #ifdef __cplusplus
 extern "C" {
@@ -169,7 +174,15 @@
 #if defined(MBEDTLS_ECDH_C)
     mbedtls_ecdh_context ecdh_ctx;              /*!<  ECDH key exchange       */
 #endif
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    mbedtls_ecjpake_context ecjpake_ctx;        /*!< EC J-PAKE key exchange */
+#if defined(MBEDTLS_SSL_CLI_C)
+    unsigned char *ecjpake_cache;               /*!< Cache for ClientHello ext */
+    size_t ecjpake_cache_len;                   /*!< Length of cached data */
+#endif
+#endif
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
     const mbedtls_ecp_curve_info **curves;      /*!<  Supported elliptic curves */
 #endif
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index da66040..58a97e8 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -20,6 +20,7 @@
     dhm.c
     ecdh.c
     ecdsa.c
+    ecjpake.c
     ecp.c
     ecp_curves.c
     entropy.c
diff --git a/library/Makefile b/library/Makefile
index f72ae8e..7d25343 100644
--- a/library/Makefile
+++ b/library/Makefile
@@ -49,7 +49,8 @@
 		bignum.o	blowfish.o	camellia.o	\
 		ccm.o		cipher.o	cipher_wrap.o	\
 		ctr_drbg.o	des.o		dhm.o		\
-		ecdh.o		ecdsa.o		ecp.o		\
+		ecdh.o		ecdsa.o		ecjpake.o	\
+		ecp.o						\
 		ecp_curves.o	entropy.o	entropy_poll.o	\
 		error.o		gcm.o		havege.o	\
 		hmac_drbg.o	md.o		md2.o		\
diff --git a/library/ecjpake.c b/library/ecjpake.c
new file mode 100644
index 0000000..1fa1c2d
--- /dev/null
+++ b/library/ecjpake.c
@@ -0,0 +1,1103 @@
+/*
+ *  Elliptic curve J-PAKE
+ *
+ *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *  SPDX-License-Identifier: Apache-2.0
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *  not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+/*
+ * References in the code are to the Thread v1.0 Specification,
+ * available to members of the Thread Group http://threadgroup.org/
+ */
+
+#if !defined(MBEDTLS_CONFIG_FILE)
+#include "mbedtls/config.h"
+#else
+#include MBEDTLS_CONFIG_FILE
+#endif
+
+#if defined(MBEDTLS_ECJPAKE_C)
+
+#include "mbedtls/ecjpake.h"
+
+#include <string.h>
+
+/*
+ * Convert a mbedtls_ecjpake_role to identifier string
+ */
+static const char * const ecjpake_id[] = {
+    "client",
+    "server"
+};
+
+#define ID_MINE     ( ecjpake_id[ ctx->role ] )
+#define ID_PEER     ( ecjpake_id[ 1 - ctx->role ] )
+
+/*
+ * Initialize context
+ */
+void mbedtls_ecjpake_init( mbedtls_ecjpake_context *ctx )
+{
+    if( ctx == NULL )
+        return;
+
+    ctx->md_info = NULL;
+    mbedtls_ecp_group_init( &ctx->grp );
+    ctx->point_format = MBEDTLS_ECP_PF_UNCOMPRESSED;
+
+    mbedtls_ecp_point_init( &ctx->Xm1 );
+    mbedtls_ecp_point_init( &ctx->Xm2 );
+    mbedtls_ecp_point_init( &ctx->Xp1 );
+    mbedtls_ecp_point_init( &ctx->Xp2 );
+    mbedtls_ecp_point_init( &ctx->Xp  );
+
+    mbedtls_mpi_init( &ctx->xm1 );
+    mbedtls_mpi_init( &ctx->xm2 );
+    mbedtls_mpi_init( &ctx->s   );
+}
+
+/*
+ * Free context
+ */
+void mbedtls_ecjpake_free( mbedtls_ecjpake_context *ctx )
+{
+    if( ctx == NULL )
+        return;
+
+    ctx->md_info = NULL;
+    mbedtls_ecp_group_free( &ctx->grp );
+
+    mbedtls_ecp_point_free( &ctx->Xm1 );
+    mbedtls_ecp_point_free( &ctx->Xm2 );
+    mbedtls_ecp_point_free( &ctx->Xp1 );
+    mbedtls_ecp_point_free( &ctx->Xp2 );
+    mbedtls_ecp_point_free( &ctx->Xp  );
+
+    mbedtls_mpi_free( &ctx->xm1 );
+    mbedtls_mpi_free( &ctx->xm2 );
+    mbedtls_mpi_free( &ctx->s   );
+}
+
+/*
+ * Setup context
+ */
+int mbedtls_ecjpake_setup( mbedtls_ecjpake_context *ctx,
+                           mbedtls_ecjpake_role role,
+                           mbedtls_md_type_t hash,
+                           mbedtls_ecp_group_id curve,
+                           const unsigned char *secret,
+                           size_t len )
+{
+    int ret;
+
+    ctx->role = role;
+
+    if( ( ctx->md_info = mbedtls_md_info_from_type( hash ) ) == NULL )
+        return( MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE );
+
+    MBEDTLS_MPI_CHK( mbedtls_ecp_group_load( &ctx->grp, curve ) );
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &ctx->s, secret, len ) );
+
+cleanup:
+    if( ret != 0 )
+        mbedtls_ecjpake_free( ctx );
+
+    return( ret );
+}
+
+/*
+ * Check if context is ready for use
+ */
+int mbedtls_ecjpake_check( const mbedtls_ecjpake_context *ctx )
+{
+    if( ctx->md_info == NULL ||
+        ctx->grp.id == MBEDTLS_ECP_DP_NONE ||
+        ctx->s.p == NULL )
+    {
+        return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+    }
+
+    return( 0 );
+}
+
+/*
+ * Write a point plus its length to a buffer
+ */
+static int ecjpake_write_len_point( unsigned char **p,
+                                    const unsigned char *end,
+                                    const mbedtls_ecp_group *grp,
+                                    const int pf,
+                                    const mbedtls_ecp_point *P )
+{
+    int ret;
+    size_t len;
+
+    /* Need at least 4 for length plus 1 for point */
+    if( end < *p || end - *p < 5 )
+        return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL );
+
+    ret = mbedtls_ecp_point_write_binary( grp, P, pf,
+                                          &len, *p + 4, end - ( *p + 4 ) );
+    if( ret != 0 )
+        return( ret );
+
+    (*p)[0] = (unsigned char)( ( len >> 24 ) & 0xFF );
+    (*p)[1] = (unsigned char)( ( len >> 16 ) & 0xFF );
+    (*p)[2] = (unsigned char)( ( len >>  8 ) & 0xFF );
+    (*p)[3] = (unsigned char)( ( len       ) & 0xFF );
+
+    *p += 4 + len;
+
+    return( 0 );
+}
+
+/*
+ * Size of the temporary buffer for ecjpake_hash:
+ * 3 EC points plus their length, plus ID and its length (4 + 6 bytes)
+ */
+#define ECJPAKE_HASH_BUF_LEN    ( 3 * ( 4 + MBEDTLS_ECP_MAX_PT_LEN ) + 4 + 6 )
+
+/*
+ * Compute hash for ZKP (7.4.2.2.2.1)
+ */
+static int ecjpake_hash( const mbedtls_md_info_t *md_info,
+                         const mbedtls_ecp_group *grp,
+                         const int pf,
+                         const mbedtls_ecp_point *G,
+                         const mbedtls_ecp_point *V,
+                         const mbedtls_ecp_point *X,
+                         const char *id,
+                         mbedtls_mpi *h )
+{
+    int ret;
+    unsigned char buf[ECJPAKE_HASH_BUF_LEN];
+    unsigned char *p = buf;
+    const unsigned char *end = buf + sizeof( buf );
+    const size_t id_len = strlen( id );
+    unsigned char hash[MBEDTLS_MD_MAX_SIZE];
+
+    /* Write things to temporary buffer */
+    MBEDTLS_MPI_CHK( ecjpake_write_len_point( &p, end, grp, pf, G ) );
+    MBEDTLS_MPI_CHK( ecjpake_write_len_point( &p, end, grp, pf, V ) );
+    MBEDTLS_MPI_CHK( ecjpake_write_len_point( &p, end, grp, pf, X ) );
+
+    if( end - p < 4 )
+        return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL );
+
+    *p++ = (unsigned char)( ( id_len >> 24 ) & 0xFF );
+    *p++ = (unsigned char)( ( id_len >> 16 ) & 0xFF );
+    *p++ = (unsigned char)( ( id_len >>  8 ) & 0xFF );
+    *p++ = (unsigned char)( ( id_len       ) & 0xFF );
+
+    if( end < p || (size_t)( end - p ) < id_len )
+        return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL );
+
+    memcpy( p, id, id_len );
+    p += id_len;
+
+    /* Compute hash */
+    mbedtls_md( md_info, buf, p - buf, hash );
+
+    /* Turn it into an integer mod n */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( h, hash,
+                                        mbedtls_md_get_size( md_info ) ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( h, h, &grp->N ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Parse a ECShnorrZKP (7.4.2.2.2) and verify it (7.4.2.3.3)
+ */
+static int ecjpake_zkp_read( const mbedtls_md_info_t *md_info,
+                             const mbedtls_ecp_group *grp,
+                             const int pf,
+                             const mbedtls_ecp_point *G,
+                             const mbedtls_ecp_point *X,
+                             const char *id,
+                             const unsigned char **p,
+                             const unsigned char *end )
+{
+    int ret;
+    mbedtls_ecp_point V, VV;
+    mbedtls_mpi r, h;
+    size_t r_len;
+
+    mbedtls_ecp_point_init( &V );
+    mbedtls_ecp_point_init( &VV );
+    mbedtls_mpi_init( &r );
+    mbedtls_mpi_init( &h );
+
+    /*
+     * struct {
+     *     ECPoint V;
+     *     opaque r<1..2^8-1>;
+     * } ECSchnorrZKP;
+     */
+    if( end < *p )
+        return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+
+    MBEDTLS_MPI_CHK( mbedtls_ecp_tls_read_point( grp, &V, p, end - *p ) );
+
+    if( end < *p || (size_t)( end - *p ) < 1 )
+    {
+        ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
+        goto cleanup;
+    }
+
+    r_len = *(*p)++;
+
+    if( end < *p || (size_t)( end - *p ) < r_len )
+    {
+        ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
+        goto cleanup;
+    }
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &r, *p, r_len ) );
+    *p += r_len;
+
+    /*
+     * Verification
+     */
+    MBEDTLS_MPI_CHK( ecjpake_hash( md_info, grp, pf, G, &V, X, id, &h ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_muladd( (mbedtls_ecp_group *) grp,
+                     &VV, &h, X, &r, G ) );
+
+    if( mbedtls_ecp_point_cmp( &VV, &V ) != 0 )
+    {
+        ret = MBEDTLS_ERR_ECP_VERIFY_FAILED;
+        goto cleanup;
+    }
+
+cleanup:
+    mbedtls_ecp_point_free( &V );
+    mbedtls_ecp_point_free( &VV );
+    mbedtls_mpi_free( &r );
+    mbedtls_mpi_free( &h );
+
+    return( ret );
+}
+
+/*
+ * Generate ZKP (7.4.2.3.2) and write it as ECSchnorrZKP (7.4.2.2.2)
+ */
+static int ecjpake_zkp_write( const mbedtls_md_info_t *md_info,
+                              const mbedtls_ecp_group *grp,
+                              const int pf, 
+                              const mbedtls_ecp_point *G,
+                              const mbedtls_mpi *x,
+                              const mbedtls_ecp_point *X,
+                              const char *id,
+                              unsigned char **p,
+                              const unsigned char *end,
+                              int (*f_rng)(void *, unsigned char *, size_t),
+                              void *p_rng )
+{
+    int ret;
+    mbedtls_ecp_point V;
+    mbedtls_mpi v;
+    mbedtls_mpi h; /* later recycled to hold r */
+    size_t len;
+
+    if( end < *p )
+        return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL );
+
+    mbedtls_ecp_point_init( &V );
+    mbedtls_mpi_init( &v );
+    mbedtls_mpi_init( &h );
+
+    /* Compute signature */
+    MBEDTLS_MPI_CHK( mbedtls_ecp_gen_keypair_base( (mbedtls_ecp_group *) grp,
+                                                   G, &v, &V, f_rng, p_rng ) );
+    MBEDTLS_MPI_CHK( ecjpake_hash( md_info, grp, pf, G, &V, X, id, &h ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &h, &h, x ) ); /* x*h */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &h, &v, &h ) ); /* v - x*h */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &h, &h, &grp->N ) ); /* r */
+
+    /* Write it out */
+    MBEDTLS_MPI_CHK( mbedtls_ecp_tls_write_point( grp, &V,
+                pf, &len, *p, end - *p ) );
+    *p += len;
+
+    len = mbedtls_mpi_size( &h ); /* actually r */
+    if( end < *p || (size_t)( end - *p ) < 1 + len || len > 255 )
+    {
+        ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+
+    *(*p)++ = (unsigned char)( len & 0xFF );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &h, *p, len ) ); /* r */
+    *p += len;
+
+cleanup:
+    mbedtls_ecp_point_free( &V );
+    mbedtls_mpi_free( &v );
+    mbedtls_mpi_free( &h );
+
+    return( ret );
+}
+
+/*
+ * Parse a ECJPAKEKeyKP (7.4.2.2.1) and check proof
+ * Output: verified public key X
+ */
+static int ecjpake_kkp_read( const mbedtls_md_info_t *md_info,
+                             const mbedtls_ecp_group *grp,
+                             const int pf,
+                             const mbedtls_ecp_point *G,
+                             mbedtls_ecp_point *X,
+                             const char *id,
+                             const unsigned char **p,
+                             const unsigned char *end )
+{
+    int ret;
+
+    if( end < *p )
+        return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+
+    /*
+     * struct {
+     *     ECPoint X;
+     *     ECSchnorrZKP zkp;
+     * } ECJPAKEKeyKP;
+     */
+    MBEDTLS_MPI_CHK( mbedtls_ecp_tls_read_point( grp, X, p, end - *p ) );
+    if( mbedtls_ecp_is_zero( X ) )
+    {
+        ret = MBEDTLS_ERR_ECP_INVALID_KEY;
+        goto cleanup;
+    }
+
+    MBEDTLS_MPI_CHK( ecjpake_zkp_read( md_info, grp, pf, G, X, id, p, end ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Generate an ECJPAKEKeyKP
+ * Output: the serialized structure, plus private/public key pair
+ */
+static int ecjpake_kkp_write( const mbedtls_md_info_t *md_info,
+                              const mbedtls_ecp_group *grp,
+                              const int pf,
+                              const mbedtls_ecp_point *G,
+                              mbedtls_mpi *x,
+                              mbedtls_ecp_point *X,
+                              const char *id,
+                              unsigned char **p,
+                              const unsigned char *end,
+                              int (*f_rng)(void *, unsigned char *, size_t),
+                              void *p_rng )
+{
+    int ret;
+    size_t len;
+
+    if( end < *p )
+        return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL );
+
+    /* Generate key (7.4.2.3.1) and write it out */
+    MBEDTLS_MPI_CHK( mbedtls_ecp_gen_keypair_base( (mbedtls_ecp_group *) grp, G, x, X,
+                                                   f_rng, p_rng ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_tls_write_point( grp, X,
+                pf, &len, *p, end - *p ) );
+    *p += len;
+
+    /* Generate and write proof */
+    MBEDTLS_MPI_CHK( ecjpake_zkp_write( md_info, grp, pf, G, x, X, id,
+                                        p, end, f_rng, p_rng ) );
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Read a ECJPAKEKeyKPPairList (7.4.2.3) and check proofs
+ * Ouputs: verified peer public keys Xa, Xb
+ */
+static int ecjpake_kkpp_read( const mbedtls_md_info_t *md_info,
+                              const mbedtls_ecp_group *grp,
+                              const int pf,
+                              const mbedtls_ecp_point *G,
+                              mbedtls_ecp_point *Xa,
+                              mbedtls_ecp_point *Xb,
+                              const char *id,
+                              const unsigned char *buf,
+                              size_t len )
+{
+    int ret;
+    const unsigned char *p = buf;
+    const unsigned char *end = buf + len;
+
+    /*
+     * struct {
+     *     ECJPAKEKeyKP ecjpake_key_kp_pair_list[2];
+     * } ECJPAKEKeyKPPairList;
+     */
+    MBEDTLS_MPI_CHK( ecjpake_kkp_read( md_info, grp, pf, G, Xa, id, &p, end ) );
+    MBEDTLS_MPI_CHK( ecjpake_kkp_read( md_info, grp, pf, G, Xb, id, &p, end ) );
+
+    if( p != end )
+        ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Generate a ECJPAKEKeyKPPairList
+ * Outputs: the serialized structure, plus two private/public key pairs
+ */
+static int ecjpake_kkpp_write( const mbedtls_md_info_t *md_info,
+                               const mbedtls_ecp_group *grp,
+                               const int pf,
+                               const mbedtls_ecp_point *G,
+                               mbedtls_mpi *xm1,
+                               mbedtls_ecp_point *Xa,
+                               mbedtls_mpi *xm2,
+                               mbedtls_ecp_point *Xb,
+                               const char *id,
+                               unsigned char *buf,
+                               size_t len,
+                               size_t *olen,
+                               int (*f_rng)(void *, unsigned char *, size_t),
+                               void *p_rng )
+{
+    int ret;
+    unsigned char *p = buf;
+    const unsigned char *end = buf + len;
+
+    MBEDTLS_MPI_CHK( ecjpake_kkp_write( md_info, grp, pf, G, xm1, Xa, id,
+                &p, end, f_rng, p_rng ) );
+    MBEDTLS_MPI_CHK( ecjpake_kkp_write( md_info, grp, pf, G, xm2, Xb, id,
+                &p, end, f_rng, p_rng ) );
+
+    *olen = p - buf;
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Read and process the first round message
+ */
+int mbedtls_ecjpake_read_round_one( mbedtls_ecjpake_context *ctx,
+                                    const unsigned char *buf,
+                                    size_t len )
+{
+    return( ecjpake_kkpp_read( ctx->md_info, &ctx->grp, ctx->point_format,
+                               &ctx->grp.G,
+                               &ctx->Xp1, &ctx->Xp2, ID_PEER,
+                               buf, len ) );
+}
+
+/*
+ * Generate and write the first round message
+ */
+int mbedtls_ecjpake_write_round_one( mbedtls_ecjpake_context *ctx,
+                            unsigned char *buf, size_t len, size_t *olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng )
+{
+    return( ecjpake_kkpp_write( ctx->md_info, &ctx->grp, ctx->point_format,
+                                &ctx->grp.G,
+                                &ctx->xm1, &ctx->Xm1, &ctx->xm2, &ctx->Xm2,
+                                ID_MINE, buf, len, olen, f_rng, p_rng ) );
+}
+
+/*
+ * Compute the sum of three points R = A + B + C
+ */
+static int ecjpake_ecp_add3( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
+                             const mbedtls_ecp_point *A,
+                             const mbedtls_ecp_point *B,
+                             const mbedtls_ecp_point *C )
+{
+    int ret;
+    mbedtls_mpi one;
+
+    mbedtls_mpi_init( &one );
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &one, 1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_muladd( grp, R, &one, A, &one, B ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_muladd( grp, R, &one, R, &one, C ) );
+
+cleanup:
+    mbedtls_mpi_free( &one );
+
+    return( ret );
+}
+
+/*
+ * Read and process second round message (C: 7.4.2.5, S: 7.4.2.6)
+ */
+int mbedtls_ecjpake_read_round_two( mbedtls_ecjpake_context *ctx,
+                                            const unsigned char *buf,
+                                            size_t len )
+{
+    int ret;
+    const unsigned char *p = buf;
+    const unsigned char *end = buf + len;
+    mbedtls_ecp_group grp;
+    mbedtls_ecp_point G;    /* C: GB, S: GA */
+
+    mbedtls_ecp_group_init( &grp );
+    mbedtls_ecp_point_init( &G );
+
+    /*
+     * Server: GA = X3  + X4  + X1      (7.4.2.6.1)
+     * Client: GB = X1  + X2  + X3      (7.4.2.5.1)
+     * Unified: G = Xm1 + Xm2 + Xp1
+     * We need that before parsing in order to check Xp as we read it
+     */
+    MBEDTLS_MPI_CHK( ecjpake_ecp_add3( &ctx->grp, &G,
+                                       &ctx->Xm1, &ctx->Xm2, &ctx->Xp1 ) );
+
+    /*
+     * struct {
+     *     ECParameters curve_params;   // only client reading server msg
+     *     ECJPAKEKeyKP ecjpake_key_kp;
+     * } Client/ServerECJPAKEParams;
+     */
+    if( ctx->role == MBEDTLS_ECJPAKE_CLIENT )
+    {
+        MBEDTLS_MPI_CHK( mbedtls_ecp_tls_read_group( &grp, &p, len ) );
+        if( grp.id != ctx->grp.id )
+        {
+            ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE;
+            goto cleanup;
+        }
+    }
+
+    MBEDTLS_MPI_CHK( ecjpake_kkp_read( ctx->md_info, &ctx->grp,
+                            ctx->point_format,
+                            &G, &ctx->Xp, ID_PEER, &p, end ) );
+
+    if( p != end )
+    {
+        ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
+        goto cleanup;
+    }
+
+cleanup:
+    mbedtls_ecp_group_free( &grp );
+    mbedtls_ecp_point_free( &G );
+
+    return( ret );
+}
+
+/*
+ * Compute R = +/- X * S mod N, taking care not to leak S
+ */
+static int ecjpake_mul_secret( mbedtls_mpi *R, int sign,
+                               const mbedtls_mpi *X,
+                               const mbedtls_mpi *S,
+                               const mbedtls_mpi *N,
+                               int (*f_rng)(void *, unsigned char *, size_t),
+                               void *p_rng )
+{
+    int ret;
+    mbedtls_mpi b; /* Blinding value, then s + N * blinding */
+
+    mbedtls_mpi_init( &b );
+
+    /* b = s + rnd-128-bit * N */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( &b, 16, f_rng, p_rng ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &b, &b, N ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &b, &b, S ) );
+
+    /* R = sign * X * b mod N */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( R, X, &b ) );
+    R->s *= sign;
+    MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( R, R, N ) );
+
+cleanup:
+    mbedtls_mpi_free( &b );
+
+    return( ret );
+}
+
+/*
+ * Generate and write the second round message (S: 7.4.2.5, C: 7.4.2.6)
+ */
+int mbedtls_ecjpake_write_round_two( mbedtls_ecjpake_context *ctx,
+                            unsigned char *buf, size_t len, size_t *olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng )
+{
+    int ret;
+    mbedtls_ecp_point G;    /* C: GA, S: GB */
+    mbedtls_ecp_point Xm;   /* C: Xc, S: Xs */
+    mbedtls_mpi xm;         /* C: xc, S: xs */
+    unsigned char *p = buf;
+    const unsigned char *end = buf + len;
+    size_t ec_len;
+
+    mbedtls_ecp_point_init( &G );
+    mbedtls_ecp_point_init( &Xm );
+    mbedtls_mpi_init( &xm );
+
+    /*
+     * First generate private/public key pair (S: 7.4.2.5.1, C: 7.4.2.6.1)
+     *
+     * Client:  GA = X1  + X3  + X4  | xs = x2  * s | Xc = xc * GA
+     * Server:  GB = X3  + X1  + X2  | xs = x4  * s | Xs = xs * GB
+     * Unified: G  = Xm1 + Xp1 + Xp2 | xm = xm2 * s | Xm = xm * G
+     */
+    MBEDTLS_MPI_CHK( ecjpake_ecp_add3( &ctx->grp, &G,
+                                       &ctx->Xp1, &ctx->Xp2, &ctx->Xm1 ) );
+    MBEDTLS_MPI_CHK( ecjpake_mul_secret( &xm, 1, &ctx->xm2, &ctx->s,
+                                         &ctx->grp.N, f_rng, p_rng ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_mul( &ctx->grp, &Xm, &xm, &G, f_rng, p_rng ) );
+
+    /*
+     * Now write things out
+     *
+     * struct {
+     *     ECParameters curve_params;   // only server writing its message
+     *     ECJPAKEKeyKP ecjpake_key_kp;
+     * } Client/ServerECJPAKEParams;
+     */
+    if( ctx->role == MBEDTLS_ECJPAKE_SERVER )
+    {
+        if( end < p )
+        {
+            ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL;
+            goto cleanup;
+        }
+        MBEDTLS_MPI_CHK( mbedtls_ecp_tls_write_group( &ctx->grp, &ec_len,
+                                                      p, end - p ) );
+        p += ec_len;
+    }
+
+    if( end < p )
+    {
+        ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL;
+        goto cleanup;
+    }
+    MBEDTLS_MPI_CHK( mbedtls_ecp_tls_write_point( &ctx->grp, &Xm,
+                     ctx->point_format, &ec_len, p, end - p ) );
+    p += ec_len;
+
+    MBEDTLS_MPI_CHK( ecjpake_zkp_write( ctx->md_info, &ctx->grp,
+                                        ctx->point_format,
+                                        &G, &xm, &Xm, ID_MINE,
+                                        &p, end, f_rng, p_rng ) );
+
+    *olen = p - buf;
+
+cleanup:
+    mbedtls_ecp_point_free( &G );
+    mbedtls_ecp_point_free( &Xm );
+    mbedtls_mpi_free( &xm );
+
+    return( ret );
+}
+
+/*
+ * Derive PMS (7.4.2.7 / 7.4.2.8)
+ */
+int mbedtls_ecjpake_derive_secret( mbedtls_ecjpake_context *ctx,
+                            unsigned char *buf, size_t len, size_t *olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng )
+{
+    int ret;
+    mbedtls_ecp_point K;
+    mbedtls_mpi m_xm2_s, one;
+    unsigned char kx[MBEDTLS_ECP_MAX_BYTES];
+    size_t x_bytes;
+
+    *olen = mbedtls_md_get_size( ctx->md_info );
+    if( len < *olen )
+        return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL );
+
+    mbedtls_ecp_point_init( &K );
+    mbedtls_mpi_init( &m_xm2_s );
+    mbedtls_mpi_init( &one );
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &one, 1 ) );
+
+    /*
+     * Client:  K = ( Xs - X4  * x2  * s ) * x2
+     * Server:  K = ( Xc - X2  * x4  * s ) * x4
+     * Unified: K = ( Xp - Xp2 * xm2 * s ) * xm2
+     */
+    MBEDTLS_MPI_CHK( ecjpake_mul_secret( &m_xm2_s, -1, &ctx->xm2, &ctx->s,
+                                         &ctx->grp.N, f_rng, p_rng ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_muladd( &ctx->grp, &K,
+                                         &one, &ctx->Xp,
+                                         &m_xm2_s, &ctx->Xp2 ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_mul( &ctx->grp, &K, &ctx->xm2, &K,
+                                      f_rng, p_rng ) );
+
+    /* PMS = SHA-256( K.X ) */
+    x_bytes = ( ctx->grp.pbits + 7 ) / 8;
+    MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &K.X, kx, x_bytes ) );
+    MBEDTLS_MPI_CHK( mbedtls_md( ctx->md_info, kx, x_bytes, buf ) );
+
+cleanup:
+    mbedtls_ecp_point_free( &K );
+    mbedtls_mpi_free( &m_xm2_s );
+    mbedtls_mpi_free( &one );
+
+    return( ret );
+}
+
+#undef ID_MINE
+#undef ID_PEER
+
+
+#if defined(MBEDTLS_SELF_TEST)
+
+#if defined(MBEDTLS_PLATFORM_C)
+#include "mbedtls/platform.h"
+#else
+#include <stdio.h>
+#define mbedtls_printf     printf
+#endif
+
+#if !defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || \
+    !defined(MBEDTLS_SHA256_C)
+int mbedtls_ecjpake_self_test( int verbose )
+{
+    (void) verbose;
+    return( 0 );
+}
+#else
+
+static const unsigned char ecjpake_test_password[] = {
+    0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x6a, 0x70, 0x61, 0x6b, 0x65, 0x74,
+    0x65, 0x73, 0x74
+};
+
+static const unsigned char ecjpake_test_x1[] = {
+    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+    0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+    0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x21
+};
+
+static const unsigned char ecjpake_test_x2[] = {
+    0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+    0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+    0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x81
+};
+
+static const unsigned char ecjpake_test_x3[] = {
+    0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+    0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+    0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x81
+};
+
+static const unsigned char ecjpake_test_x4[] = {
+    0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc,
+    0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
+    0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe1
+};
+
+static const unsigned char ecjpake_test_cli_one[] = {
+    0x41, 0x04, 0xac, 0xcf, 0x01, 0x06, 0xef, 0x85, 0x8f, 0xa2, 0xd9, 0x19,
+    0x33, 0x13, 0x46, 0x80, 0x5a, 0x78, 0xb5, 0x8b, 0xba, 0xd0, 0xb8, 0x44,
+    0xe5, 0xc7, 0x89, 0x28, 0x79, 0x14, 0x61, 0x87, 0xdd, 0x26, 0x66, 0xad,
+    0xa7, 0x81, 0xbb, 0x7f, 0x11, 0x13, 0x72, 0x25, 0x1a, 0x89, 0x10, 0x62,
+    0x1f, 0x63, 0x4d, 0xf1, 0x28, 0xac, 0x48, 0xe3, 0x81, 0xfd, 0x6e, 0xf9,
+    0x06, 0x07, 0x31, 0xf6, 0x94, 0xa4, 0x41, 0x04, 0x1d, 0xd0, 0xbd, 0x5d,
+    0x45, 0x66, 0xc9, 0xbe, 0xd9, 0xce, 0x7d, 0xe7, 0x01, 0xb5, 0xe8, 0x2e,
+    0x08, 0xe8, 0x4b, 0x73, 0x04, 0x66, 0x01, 0x8a, 0xb9, 0x03, 0xc7, 0x9e,
+    0xb9, 0x82, 0x17, 0x22, 0x36, 0xc0, 0xc1, 0x72, 0x8a, 0xe4, 0xbf, 0x73,
+    0x61, 0x0d, 0x34, 0xde, 0x44, 0x24, 0x6e, 0xf3, 0xd9, 0xc0, 0x5a, 0x22,
+    0x36, 0xfb, 0x66, 0xa6, 0x58, 0x3d, 0x74, 0x49, 0x30, 0x8b, 0xab, 0xce,
+    0x20, 0x72, 0xfe, 0x16, 0x66, 0x29, 0x92, 0xe9, 0x23, 0x5c, 0x25, 0x00,
+    0x2f, 0x11, 0xb1, 0x50, 0x87, 0xb8, 0x27, 0x38, 0xe0, 0x3c, 0x94, 0x5b,
+    0xf7, 0xa2, 0x99, 0x5d, 0xda, 0x1e, 0x98, 0x34, 0x58, 0x41, 0x04, 0x7e,
+    0xa6, 0xe3, 0xa4, 0x48, 0x70, 0x37, 0xa9, 0xe0, 0xdb, 0xd7, 0x92, 0x62,
+    0xb2, 0xcc, 0x27, 0x3e, 0x77, 0x99, 0x30, 0xfc, 0x18, 0x40, 0x9a, 0xc5,
+    0x36, 0x1c, 0x5f, 0xe6, 0x69, 0xd7, 0x02, 0xe1, 0x47, 0x79, 0x0a, 0xeb,
+    0x4c, 0xe7, 0xfd, 0x65, 0x75, 0xab, 0x0f, 0x6c, 0x7f, 0xd1, 0xc3, 0x35,
+    0x93, 0x9a, 0xa8, 0x63, 0xba, 0x37, 0xec, 0x91, 0xb7, 0xe3, 0x2b, 0xb0,
+    0x13, 0xbb, 0x2b, 0x41, 0x04, 0xa4, 0x95, 0x58, 0xd3, 0x2e, 0xd1, 0xeb,
+    0xfc, 0x18, 0x16, 0xaf, 0x4f, 0xf0, 0x9b, 0x55, 0xfc, 0xb4, 0xca, 0x47,
+    0xb2, 0xa0, 0x2d, 0x1e, 0x7c, 0xaf, 0x11, 0x79, 0xea, 0x3f, 0xe1, 0x39,
+    0x5b, 0x22, 0xb8, 0x61, 0x96, 0x40, 0x16, 0xfa, 0xba, 0xf7, 0x2c, 0x97,
+    0x56, 0x95, 0xd9, 0x3d, 0x4d, 0xf0, 0xe5, 0x19, 0x7f, 0xe9, 0xf0, 0x40,
+    0x63, 0x4e, 0xd5, 0x97, 0x64, 0x93, 0x77, 0x87, 0xbe, 0x20, 0xbc, 0x4d,
+    0xee, 0xbb, 0xf9, 0xb8, 0xd6, 0x0a, 0x33, 0x5f, 0x04, 0x6c, 0xa3, 0xaa,
+    0x94, 0x1e, 0x45, 0x86, 0x4c, 0x7c, 0xad, 0xef, 0x9c, 0xf7, 0x5b, 0x3d,
+    0x8b, 0x01, 0x0e, 0x44, 0x3e, 0xf0
+};
+
+static const unsigned char ecjpake_test_srv_one[] = {
+    0x41, 0x04, 0x7e, 0xa6, 0xe3, 0xa4, 0x48, 0x70, 0x37, 0xa9, 0xe0, 0xdb,
+    0xd7, 0x92, 0x62, 0xb2, 0xcc, 0x27, 0x3e, 0x77, 0x99, 0x30, 0xfc, 0x18,
+    0x40, 0x9a, 0xc5, 0x36, 0x1c, 0x5f, 0xe6, 0x69, 0xd7, 0x02, 0xe1, 0x47,
+    0x79, 0x0a, 0xeb, 0x4c, 0xe7, 0xfd, 0x65, 0x75, 0xab, 0x0f, 0x6c, 0x7f,
+    0xd1, 0xc3, 0x35, 0x93, 0x9a, 0xa8, 0x63, 0xba, 0x37, 0xec, 0x91, 0xb7,
+    0xe3, 0x2b, 0xb0, 0x13, 0xbb, 0x2b, 0x41, 0x04, 0x09, 0xf8, 0x5b, 0x3d,
+    0x20, 0xeb, 0xd7, 0x88, 0x5c, 0xe4, 0x64, 0xc0, 0x8d, 0x05, 0x6d, 0x64,
+    0x28, 0xfe, 0x4d, 0xd9, 0x28, 0x7a, 0xa3, 0x65, 0xf1, 0x31, 0xf4, 0x36,
+    0x0f, 0xf3, 0x86, 0xd8, 0x46, 0x89, 0x8b, 0xc4, 0xb4, 0x15, 0x83, 0xc2,
+    0xa5, 0x19, 0x7f, 0x65, 0xd7, 0x87, 0x42, 0x74, 0x6c, 0x12, 0xa5, 0xec,
+    0x0a, 0x4f, 0xfe, 0x2f, 0x27, 0x0a, 0x75, 0x0a, 0x1d, 0x8f, 0xb5, 0x16,
+    0x20, 0x93, 0x4d, 0x74, 0xeb, 0x43, 0xe5, 0x4d, 0xf4, 0x24, 0xfd, 0x96,
+    0x30, 0x6c, 0x01, 0x17, 0xbf, 0x13, 0x1a, 0xfa, 0xbf, 0x90, 0xa9, 0xd3,
+    0x3d, 0x11, 0x98, 0xd9, 0x05, 0x19, 0x37, 0x35, 0x14, 0x41, 0x04, 0x19,
+    0x0a, 0x07, 0x70, 0x0f, 0xfa, 0x4b, 0xe6, 0xae, 0x1d, 0x79, 0xee, 0x0f,
+    0x06, 0xae, 0xb5, 0x44, 0xcd, 0x5a, 0xdd, 0xaa, 0xbe, 0xdf, 0x70, 0xf8,
+    0x62, 0x33, 0x21, 0x33, 0x2c, 0x54, 0xf3, 0x55, 0xf0, 0xfb, 0xfe, 0xc7,
+    0x83, 0xed, 0x35, 0x9e, 0x5d, 0x0b, 0xf7, 0x37, 0x7a, 0x0f, 0xc4, 0xea,
+    0x7a, 0xce, 0x47, 0x3c, 0x9c, 0x11, 0x2b, 0x41, 0xcc, 0xd4, 0x1a, 0xc5,
+    0x6a, 0x56, 0x12, 0x41, 0x04, 0x36, 0x0a, 0x1c, 0xea, 0x33, 0xfc, 0xe6,
+    0x41, 0x15, 0x64, 0x58, 0xe0, 0xa4, 0xea, 0xc2, 0x19, 0xe9, 0x68, 0x31,
+    0xe6, 0xae, 0xbc, 0x88, 0xb3, 0xf3, 0x75, 0x2f, 0x93, 0xa0, 0x28, 0x1d,
+    0x1b, 0xf1, 0xfb, 0x10, 0x60, 0x51, 0xdb, 0x96, 0x94, 0xa8, 0xd6, 0xe8,
+    0x62, 0xa5, 0xef, 0x13, 0x24, 0xa3, 0xd9, 0xe2, 0x78, 0x94, 0xf1, 0xee,
+    0x4f, 0x7c, 0x59, 0x19, 0x99, 0x65, 0xa8, 0xdd, 0x4a, 0x20, 0x91, 0x84,
+    0x7d, 0x2d, 0x22, 0xdf, 0x3e, 0xe5, 0x5f, 0xaa, 0x2a, 0x3f, 0xb3, 0x3f,
+    0xd2, 0xd1, 0xe0, 0x55, 0xa0, 0x7a, 0x7c, 0x61, 0xec, 0xfb, 0x8d, 0x80,
+    0xec, 0x00, 0xc2, 0xc9, 0xeb, 0x12
+};
+
+static const unsigned char ecjpake_test_srv_two[] = {
+    0x03, 0x00, 0x17, 0x41, 0x04, 0x0f, 0xb2, 0x2b, 0x1d, 0x5d, 0x11, 0x23,
+    0xe0, 0xef, 0x9f, 0xeb, 0x9d, 0x8a, 0x2e, 0x59, 0x0a, 0x1f, 0x4d, 0x7c,
+    0xed, 0x2c, 0x2b, 0x06, 0x58, 0x6e, 0x8f, 0x2a, 0x16, 0xd4, 0xeb, 0x2f,
+    0xda, 0x43, 0x28, 0xa2, 0x0b, 0x07, 0xd8, 0xfd, 0x66, 0x76, 0x54, 0xca,
+    0x18, 0xc5, 0x4e, 0x32, 0xa3, 0x33, 0xa0, 0x84, 0x54, 0x51, 0xe9, 0x26,
+    0xee, 0x88, 0x04, 0xfd, 0x7a, 0xf0, 0xaa, 0xa7, 0xa6, 0x41, 0x04, 0x55,
+    0x16, 0xea, 0x3e, 0x54, 0xa0, 0xd5, 0xd8, 0xb2, 0xce, 0x78, 0x6b, 0x38,
+    0xd3, 0x83, 0x37, 0x00, 0x29, 0xa5, 0xdb, 0xe4, 0x45, 0x9c, 0x9d, 0xd6,
+    0x01, 0xb4, 0x08, 0xa2, 0x4a, 0xe6, 0x46, 0x5c, 0x8a, 0xc9, 0x05, 0xb9,
+    0xeb, 0x03, 0xb5, 0xd3, 0x69, 0x1c, 0x13, 0x9e, 0xf8, 0x3f, 0x1c, 0xd4,
+    0x20, 0x0f, 0x6c, 0x9c, 0xd4, 0xec, 0x39, 0x22, 0x18, 0xa5, 0x9e, 0xd2,
+    0x43, 0xd3, 0xc8, 0x20, 0xff, 0x72, 0x4a, 0x9a, 0x70, 0xb8, 0x8c, 0xb8,
+    0x6f, 0x20, 0xb4, 0x34, 0xc6, 0x86, 0x5a, 0xa1, 0xcd, 0x79, 0x06, 0xdd,
+    0x7c, 0x9b, 0xce, 0x35, 0x25, 0xf5, 0x08, 0x27, 0x6f, 0x26, 0x83, 0x6c
+};
+
+static const unsigned char ecjpake_test_cli_two[] = {
+    0x41, 0x04, 0x69, 0xd5, 0x4e, 0xe8, 0x5e, 0x90, 0xce, 0x3f, 0x12, 0x46,
+    0x74, 0x2d, 0xe5, 0x07, 0xe9, 0x39, 0xe8, 0x1d, 0x1d, 0xc1, 0xc5, 0xcb,
+    0x98, 0x8b, 0x58, 0xc3, 0x10, 0xc9, 0xfd, 0xd9, 0x52, 0x4d, 0x93, 0x72,
+    0x0b, 0x45, 0x54, 0x1c, 0x83, 0xee, 0x88, 0x41, 0x19, 0x1d, 0xa7, 0xce,
+    0xd8, 0x6e, 0x33, 0x12, 0xd4, 0x36, 0x23, 0xc1, 0xd6, 0x3e, 0x74, 0x98,
+    0x9a, 0xba, 0x4a, 0xff, 0xd1, 0xee, 0x41, 0x04, 0x07, 0x7e, 0x8c, 0x31,
+    0xe2, 0x0e, 0x6b, 0xed, 0xb7, 0x60, 0xc1, 0x35, 0x93, 0xe6, 0x9f, 0x15,
+    0xbe, 0x85, 0xc2, 0x7d, 0x68, 0xcd, 0x09, 0xcc, 0xb8, 0xc4, 0x18, 0x36,
+    0x08, 0x91, 0x7c, 0x5c, 0x3d, 0x40, 0x9f, 0xac, 0x39, 0xfe, 0xfe, 0xe8,
+    0x2f, 0x72, 0x92, 0xd3, 0x6f, 0x0d, 0x23, 0xe0, 0x55, 0x91, 0x3f, 0x45,
+    0xa5, 0x2b, 0x85, 0xdd, 0x8a, 0x20, 0x52, 0xe9, 0xe1, 0x29, 0xbb, 0x4d,
+    0x20, 0x0f, 0x01, 0x1f, 0x19, 0x48, 0x35, 0x35, 0xa6, 0xe8, 0x9a, 0x58,
+    0x0c, 0x9b, 0x00, 0x03, 0xba, 0xf2, 0x14, 0x62, 0xec, 0xe9, 0x1a, 0x82,
+    0xcc, 0x38, 0xdb, 0xdc, 0xae, 0x60, 0xd9, 0xc5, 0x4c
+};
+
+static const unsigned char ecjpake_test_pms[] = {
+    0xf3, 0xd4, 0x7f, 0x59, 0x98, 0x44, 0xdb, 0x92, 0xa5, 0x69, 0xbb, 0xe7,
+    0x98, 0x1e, 0x39, 0xd9, 0x31, 0xfd, 0x74, 0x3b, 0xf2, 0x2e, 0x98, 0xf9,
+    0xb4, 0x38, 0xf7, 0x19, 0xd3, 0xc4, 0xf3, 0x51
+};
+
+/* Load my private keys and generate the correponding public keys */
+static int ecjpake_test_load( mbedtls_ecjpake_context *ctx,
+                              const unsigned char *xm1, size_t len1,
+                              const unsigned char *xm2, size_t len2 )
+{
+    int ret;
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &ctx->xm1, xm1, len1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &ctx->xm2, xm2, len2 ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_mul( &ctx->grp, &ctx->Xm1, &ctx->xm1,
+                                      &ctx->grp.G, NULL, NULL ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_mul( &ctx->grp, &ctx->Xm2, &ctx->xm2,
+                                      &ctx->grp.G, NULL, NULL ) );
+
+cleanup:
+    return( ret );
+}
+
+/* For tests we don't need a secure RNG;
+ * use the LGC from Numerical Recipes for simplicity */
+static int ecjpake_lgc( void *p, unsigned char *out, size_t len )
+{
+    static uint32_t x = 42;
+    (void) p;
+
+    while( len > 0 )
+    {
+        size_t use_len = len > 4 ? 4 : len;
+        x = 1664525 * x + 1013904223;
+        memcpy( out, &x, use_len );
+        out += use_len;
+        len -= use_len;
+    }
+
+    return( 0 );
+}
+
+#define TEST_ASSERT( x )    \
+    do {                    \
+        if( x )             \
+            ret = 0;        \
+        else                \
+        {                   \
+            ret = 1;        \
+            goto cleanup;   \
+        }                   \
+    } while( 0 )
+
+/*
+ * Checkup routine
+ */
+int mbedtls_ecjpake_self_test( int verbose )
+{
+    int ret;
+    mbedtls_ecjpake_context cli;
+    mbedtls_ecjpake_context srv;
+    unsigned char buf[512], pms[32];
+    size_t len, pmslen;
+
+    mbedtls_ecjpake_init( &cli );
+    mbedtls_ecjpake_init( &srv );
+
+    if( verbose != 0 )
+        mbedtls_printf( "  ECJPAKE test #0 (setup): " );
+
+    TEST_ASSERT( mbedtls_ecjpake_setup( &cli, MBEDTLS_ECJPAKE_CLIENT,
+                    MBEDTLS_MD_SHA256, MBEDTLS_ECP_DP_SECP256R1,
+                    ecjpake_test_password,
+            sizeof( ecjpake_test_password ) ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_setup( &srv, MBEDTLS_ECJPAKE_SERVER,
+                    MBEDTLS_MD_SHA256, MBEDTLS_ECP_DP_SECP256R1,
+                    ecjpake_test_password,
+            sizeof( ecjpake_test_password ) ) == 0 );
+
+    if( verbose != 0 )
+        mbedtls_printf( "passed\n" );
+
+    if( verbose != 0 )
+        mbedtls_printf( "  ECJPAKE test #1 (random handshake): " );
+
+    TEST_ASSERT( mbedtls_ecjpake_write_round_one( &cli,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_read_round_one( &srv, buf, len ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_write_round_one( &srv,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_read_round_one( &cli, buf, len ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_write_round_two( &srv,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_read_round_two( &cli, buf, len ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_derive_secret( &cli,
+                 pms, sizeof( pms ), &pmslen, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_write_round_two( &cli,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_read_round_two( &srv, buf, len ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_derive_secret( &srv,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( len == pmslen );
+    TEST_ASSERT( memcmp( buf, pms, len ) == 0 );
+
+    if( verbose != 0 )
+        mbedtls_printf( "passed\n" );
+
+    if( verbose != 0 )
+        mbedtls_printf( "  ECJPAKE test #2 (reference handshake): " );
+
+    /* Simulate generation of round one */
+    MBEDTLS_MPI_CHK( ecjpake_test_load( &cli,
+                ecjpake_test_x1, sizeof( ecjpake_test_x1 ),
+                ecjpake_test_x2, sizeof( ecjpake_test_x2 ) ) );
+
+    MBEDTLS_MPI_CHK( ecjpake_test_load( &srv,
+                ecjpake_test_x3, sizeof( ecjpake_test_x3 ),
+                ecjpake_test_x4, sizeof( ecjpake_test_x4 ) ) );
+
+    /* Read round one */
+    TEST_ASSERT( mbedtls_ecjpake_read_round_one( &srv,
+                                    ecjpake_test_cli_one,
+                            sizeof( ecjpake_test_cli_one ) ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_read_round_one( &cli,
+                                    ecjpake_test_srv_one,
+                            sizeof( ecjpake_test_srv_one ) ) == 0 );
+
+    /* Skip generation of round two, read round two */
+    TEST_ASSERT( mbedtls_ecjpake_read_round_two( &cli,
+                                    ecjpake_test_srv_two,
+                            sizeof( ecjpake_test_srv_two ) ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_read_round_two( &srv,
+                                    ecjpake_test_cli_two,
+                            sizeof( ecjpake_test_cli_two ) ) == 0 );
+
+    /* Server derives PMS */
+    TEST_ASSERT( mbedtls_ecjpake_derive_secret( &srv,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( len == sizeof( ecjpake_test_pms ) );
+    TEST_ASSERT( memcmp( buf, ecjpake_test_pms, len ) == 0 );
+
+    memset( buf, 0, len ); /* Avoid interferences with next step */
+
+    /* Client derives PMS */
+    TEST_ASSERT( mbedtls_ecjpake_derive_secret( &cli,
+                 buf, sizeof( buf ), &len, ecjpake_lgc, NULL ) == 0 );
+
+    TEST_ASSERT( len == sizeof( ecjpake_test_pms ) );
+    TEST_ASSERT( memcmp( buf, ecjpake_test_pms, len ) == 0 );
+
+    if( verbose != 0 )
+        mbedtls_printf( "passed\n" );
+
+cleanup:
+    mbedtls_ecjpake_free( &cli );
+    mbedtls_ecjpake_free( &srv );
+
+    if( ret != 0 )
+    {
+        if( verbose != 0 )
+            mbedtls_printf( "failed\n" );
+
+        ret = 1;
+    }
+
+    if( verbose != 0 )
+        mbedtls_printf( "\n" );
+
+    return( ret );
+}
+
+#undef TEST_ASSERT
+
+#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED && MBEDTLS_SHA256_C */
+
+#endif /* MBEDTLS_SELF_TEST */
+
+#endif /* MBEDTLS_ECJPAKE_C */
diff --git a/library/ecp.c b/library/ecp.c
index 64a47ab..19bb488 100644
--- a/library/ecp.c
+++ b/library/ecp.c
@@ -404,6 +404,22 @@
 }
 
 /*
+ * Compare two points lazyly
+ */
+int mbedtls_ecp_point_cmp( const mbedtls_ecp_point *P,
+                           const mbedtls_ecp_point *Q )
+{
+    if( mbedtls_mpi_cmp_mpi( &P->X, &Q->X ) == 0 &&
+        mbedtls_mpi_cmp_mpi( &P->Y, &Q->Y ) == 0 &&
+        mbedtls_mpi_cmp_mpi( &P->Z, &Q->Z ) == 0 )
+    {
+        return( 0 );
+    }
+
+    return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+}
+
+/*
  * Import a non-zero point from ASCII strings
  */
 int mbedtls_ecp_point_read_string( mbedtls_ecp_point *P, int radix,
@@ -1668,7 +1684,38 @@
 #endif /* ECP_SHORTWEIERSTRASS */
 
 /*
+ * R = m * P with shortcuts for m == 1 and m == -1
+ * NOT constant-time - ONLY for short Weierstrass!
+ */
+static int mbedtls_ecp_mul_shortcuts( mbedtls_ecp_group *grp,
+                                      mbedtls_ecp_point *R,
+                                      const mbedtls_mpi *m,
+                                      const mbedtls_ecp_point *P )
+{
+    int ret;
+
+    if( mbedtls_mpi_cmp_int( m, 1 ) == 0 )
+    {
+        MBEDTLS_MPI_CHK( mbedtls_ecp_copy( R, P ) );
+    }
+    else if( mbedtls_mpi_cmp_int( m, -1 ) == 0 )
+    {
+        MBEDTLS_MPI_CHK( mbedtls_ecp_copy( R, P ) );
+        if( mbedtls_mpi_cmp_int( &R->Y, 0 ) != 0 )
+            MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &R->Y, &grp->P, &R->Y ) );
+    }
+    else
+    {
+        MBEDTLS_MPI_CHK( mbedtls_ecp_mul( grp, R, m, P, NULL, NULL ) );
+    }
+
+cleanup:
+    return( ret );
+}
+
+/*
  * Linear combination
+ * NOT constant-time
  */
 int mbedtls_ecp_muladd( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
              const mbedtls_mpi *m, const mbedtls_ecp_point *P,
@@ -1682,8 +1729,9 @@
 
     mbedtls_ecp_point_init( &mP );
 
-    MBEDTLS_MPI_CHK( mbedtls_ecp_mul( grp, &mP, m, P, NULL, NULL ) );
-    MBEDTLS_MPI_CHK( mbedtls_ecp_mul( grp, R,   n, Q, NULL, NULL ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_mul_shortcuts( grp, &mP, m, P ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_mul_shortcuts( grp, R,   n, Q ) );
+
     MBEDTLS_MPI_CHK( ecp_add_mixed( grp, R, &mP, R ) );
     MBEDTLS_MPI_CHK( ecp_normalize_jac( grp, R ) );
 
@@ -1762,9 +1810,11 @@
 }
 
 /*
- * Generate a keypair
+ * Generate a keypair with configurable base point
  */
-int mbedtls_ecp_gen_keypair( mbedtls_ecp_group *grp, mbedtls_mpi *d, mbedtls_ecp_point *Q,
+int mbedtls_ecp_gen_keypair_base( mbedtls_ecp_group *grp,
+                     const mbedtls_ecp_point *G,
+                     mbedtls_mpi *d, mbedtls_ecp_point *Q,
                      int (*f_rng)(void *, unsigned char *, size_t),
                      void *p_rng )
 {
@@ -1836,7 +1886,18 @@
     if( ret != 0 )
         return( ret );
 
-    return( mbedtls_ecp_mul( grp, Q, d, &grp->G, f_rng, p_rng ) );
+    return( mbedtls_ecp_mul( grp, Q, d, G, f_rng, p_rng ) );
+}
+
+/*
+ * Generate key pair, wrapper for conventional base point
+ */
+int mbedtls_ecp_gen_keypair( mbedtls_ecp_group *grp,
+                             mbedtls_mpi *d, mbedtls_ecp_point *Q,
+                             int (*f_rng)(void *, unsigned char *, size_t),
+                             void *p_rng )
+{
+    return( mbedtls_ecp_gen_keypair_base( grp, &grp->G, d, Q, f_rng, p_rng ) );
 }
 
 /*
diff --git a/library/pkparse.c b/library/pkparse.c
index bddcf5d..275429e 100644
--- a/library/pkparse.c
+++ b/library/pkparse.c
@@ -1181,6 +1181,7 @@
         return( ret );
 #endif /* MBEDTLS_PKCS12_C || MBEDTLS_PKCS5_C */
 #else
+    ((void) ret);
     ((void) pwd);
     ((void) pwdlen);
 #endif /* MBEDTLS_PEM_PARSE_C */
diff --git a/library/ssl_ciphersuites.c b/library/ssl_ciphersuites.c
index 8bad7ab..949b9ed 100644
--- a/library/ssl_ciphersuites.c
+++ b/library/ssl_ciphersuites.c
@@ -40,7 +40,7 @@
  *
  * Current rule (except rc4, weak and null which come last):
  * 1. By key exchange:
- *    Forward-secure non-PSK > forward-secure PSK > other non-PSK > other PSK
+ *    Forward-secure non-PSK > forward-secure PSK > ECJPAKE > other non-PSK > other PSK
  * 2. By key length and cipher:
  *    AES-256 > Camellia-256 > AES-128 > Camellia-128 > 3DES
  * 3. By cipher mode when relevant GCM > CCM > CBC > CCM_8
@@ -131,6 +131,9 @@
     MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA,
     MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
 
+    /* The ECJPAKE suite */
+    MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8,
+
     /* All AES-256 suites */
     MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
     MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
@@ -1510,6 +1513,18 @@
 #endif /* MBEDTLS_ARC4_C */
 #endif /* MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+#if defined(MBEDTLS_AES_C)
+#if defined(MBEDTLS_CCM_C)
+    { MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8, "TLS-ECJPAKE-WITH-AES-128-CCM-8",
+      MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECJPAKE,
+      MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+      MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3,
+      MBEDTLS_CIPHERSUITE_SHORT_TAG },
+#endif /* MBEDTLS_CCM_C */
+#endif /* MBEDTLS_AES_C */
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
 #if defined(MBEDTLS_ENABLE_WEAK_CIPHERSUITES)
 #if defined(MBEDTLS_CIPHER_NULL_CIPHER)
 #if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED)
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index 32eae0f..5d9af17 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -241,7 +241,8 @@
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 &&
           MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED */
 
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
 static void ssl_write_supported_elliptic_curves_ext( mbedtls_ssl_context *ssl,
                                                      unsigned char *buf,
                                                      size_t *olen )
@@ -336,7 +337,86 @@
 
     *olen = 6;
 }
-#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C */
+#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || 
+          MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+static void ssl_write_ecjpake_kkpp_ext( mbedtls_ssl_context *ssl,
+                                        unsigned char *buf,
+                                        size_t *olen )
+{
+    int ret;
+    unsigned char *p = buf;
+    const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN;
+    size_t kkpp_len;
+
+    *olen = 0;
+
+    /* Skip costly extension if we can't use EC J-PAKE anyway */
+    if( mbedtls_ecjpake_check( &ssl->handshake->ecjpake_ctx ) != 0 )
+        return;
+
+    MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, adding ecjpake_kkpp extension" ) );
+
+    if( end - p < 4 )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "buffer too small" ) );
+        return;
+    }
+
+    *p++ = (unsigned char)( ( MBEDTLS_TLS_EXT_ECJPAKE_KKPP >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( MBEDTLS_TLS_EXT_ECJPAKE_KKPP      ) & 0xFF );
+
+    /*
+     * We may need to send ClientHello multiple times for Hello verification.
+     * We don't want to compute fresh values every time (both for performance
+     * and consistency reasons), so cache the extension content.
+     */
+    if( ssl->handshake->ecjpake_cache == NULL ||
+        ssl->handshake->ecjpake_cache_len == 0 )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 3, ( "generating new ecjpake parameters" ) );
+
+        ret = mbedtls_ecjpake_write_round_one( &ssl->handshake->ecjpake_ctx,
+                                        p + 2, end - p - 2, &kkpp_len,
+                                        ssl->conf->f_rng, ssl->conf->p_rng );
+        if( ret != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1 , "mbedtls_ecjpake_write_round_one", ret );
+            return;
+        }
+
+        ssl->handshake->ecjpake_cache = mbedtls_calloc( 1, kkpp_len );
+        if( ssl->handshake->ecjpake_cache == NULL )
+        {
+            MBEDTLS_SSL_DEBUG_MSG( 1, ( "allocation failed" ) );
+            return;
+        }
+
+        memcpy( ssl->handshake->ecjpake_cache, p + 2, kkpp_len );
+        ssl->handshake->ecjpake_cache_len = kkpp_len;
+    }
+    else
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 3, ( "re-using cached ecjpake parameters" ) );
+
+        kkpp_len = ssl->handshake->ecjpake_cache_len;
+
+        if( (size_t)( end - p - 2 ) < kkpp_len )
+        {
+            MBEDTLS_SSL_DEBUG_MSG( 1, ( "buffer too small" ) );
+            return;
+        }
+
+        memcpy( p + 2, ssl->handshake->ecjpake_cache, kkpp_len );
+    }
+
+    *p++ = (unsigned char)( ( kkpp_len >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( kkpp_len      ) & 0xFF );
+
+    *olen = kkpp_len + 4;
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
 static void ssl_write_max_fragment_length_ext( mbedtls_ssl_context *ssl,
@@ -790,8 +870,14 @@
             continue;
 #endif
 
-        MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, add ciphersuite: %2d",
-                       ciphersuites[i] ) );
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+        if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE &&
+            mbedtls_ecjpake_check( &ssl->handshake->ecjpake_ctx ) != 0 )
+            continue;
+#endif
+
+        MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, add ciphersuite: %04x",
+                                    ciphersuites[i] ) );
 
         n++;
         *p++ = (unsigned char)( ciphersuites[i] >> 8 );
@@ -881,7 +967,8 @@
     ext_len += olen;
 #endif
 
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
     ssl_write_supported_elliptic_curves_ext( ssl, p + 2 + ext_len, &olen );
     ext_len += olen;
 
@@ -889,6 +976,11 @@
     ext_len += olen;
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    ssl_write_ecjpake_kkpp_ext( ssl, p + 2 + ext_len, &olen );
+    ext_len += olen;
+#endif
+
 #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
     ssl_write_max_fragment_length_ext( ssl, p + 2 + ext_len, &olen );
     ext_len += olen;
@@ -1096,7 +1188,8 @@
 }
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
 
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
 static int ssl_parse_supported_point_formats_ext( mbedtls_ssl_context *ssl,
                                                   const unsigned char *buf,
                                                   size_t len )
@@ -1117,7 +1210,12 @@
         if( p[0] == MBEDTLS_ECP_PF_UNCOMPRESSED ||
             p[0] == MBEDTLS_ECP_PF_COMPRESSED )
         {
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
             ssl->handshake->ecdh_ctx.point_format = p[0];
+#endif            
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+            ssl->handshake->ecjpake_ctx.point_format = p[0];
+#endif
             MBEDTLS_SSL_DEBUG_MSG( 4, ( "point format selected: %d", p[0] ) );
             return( 0 );
         }
@@ -1129,7 +1227,38 @@
     MBEDTLS_SSL_DEBUG_MSG( 1, ( "no point format in common" ) );
     return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO );
 }
-#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C */
+#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || 
+          MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+static int ssl_parse_ecjpake_kkpp( mbedtls_ssl_context *ssl,
+                                   const unsigned char *buf,
+                                   size_t len )
+{
+    int ret;
+
+    if( ssl->transform_negotiate->ciphersuite_info->key_exchange !=
+        MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 3, ( "skip ecjpake kkpp extension" ) );
+        return( 0 );
+    }
+
+    /* If we got here, we no longer need our cached extension */
+    mbedtls_free( ssl->handshake->ecjpake_cache );
+    ssl->handshake->ecjpake_cache = NULL;
+    ssl->handshake->ecjpake_cache_len = 0;
+
+    if( ( ret = mbedtls_ecjpake_read_round_one( &ssl->handshake->ecjpake_ctx,
+                                                buf, len ) ) != 0 )
+    {
+        MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecjpake_read_round_one", ret );
+        return( ret );
+    }
+
+    return( 0 );
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_ALPN)
 static int ssl_parse_alpn_ext( mbedtls_ssl_context *ssl,
@@ -1479,7 +1608,7 @@
     MBEDTLS_SSL_DEBUG_MSG( 3, ( "%s session has been resumed",
                    ssl->handshake->resume ? "a" : "no" ) );
 
-    MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %d", i ) );
+    MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %04x", i ) );
     MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: %d", buf[37 + n] ) );
 
     suite_info = mbedtls_ssl_ciphersuite_from_id( ssl->session_negotiate->ciphersuite );
@@ -1494,6 +1623,8 @@
         return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO );
     }
 
+    MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %s", suite_info->name ) );
+
     i = 0;
     while( 1 )
     {
@@ -1617,7 +1748,8 @@
             break;
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
 
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
         case MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS:
             MBEDTLS_SSL_DEBUG_MSG( 3, ( "found supported_point_formats extension" ) );
 
@@ -1628,7 +1760,21 @@
             }
 
             break;
-#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C */
+#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C ||
+          MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+        case MBEDTLS_TLS_EXT_ECJPAKE_KKPP:
+            MBEDTLS_SSL_DEBUG_MSG( 3, ( "found ecjpake_kkpp extension" ) );
+
+            if( ( ret = ssl_parse_ecjpake_kkpp( ssl,
+                            ext + 4, ext_size ) ) != 0 )
+            {
+                return( ret );
+            }
+
+            break;
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_ALPN)
         case MBEDTLS_TLS_EXT_ALPN:
@@ -2147,6 +2293,19 @@
 #endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED ||
           MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED ||
           MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+    {
+        ret = mbedtls_ecjpake_read_round_two( &ssl->handshake->ecjpake_ctx,
+                                              p, end - p );
+        if( ret != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecjpake_read_round_two", ret );
+            return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE );
+        }
+    }
+    else
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
     {
         MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
         return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
@@ -2351,7 +2510,8 @@
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate request" ) );
         ssl->state++;
@@ -2375,7 +2535,8 @@
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate request" ) );
         ssl->state++;
@@ -2756,6 +2917,31 @@
     }
     else
 #endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+    {
+        i = 4;
+
+        ret = mbedtls_ecjpake_write_round_two( &ssl->handshake->ecjpake_ctx,
+                ssl->out_msg + i, MBEDTLS_SSL_MAX_CONTENT_LEN - i, &n,
+                ssl->conf->f_rng, ssl->conf->p_rng );
+        if( ret != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecjpake_write_round_two", ret );
+            return( ret );
+        }
+
+        ret = mbedtls_ecjpake_derive_secret( &ssl->handshake->ecjpake_ctx,
+                ssl->handshake->premaster, 32, &ssl->handshake->pmslen,
+                ssl->conf->f_rng, ssl->conf->p_rng );
+        if( ret != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecjpake_derive_secret", ret );
+            return( ret );
+        }
+    }
+    else
+#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */
     {
         ((void) ciphersuite_info);
         MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
@@ -2799,7 +2985,8 @@
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate verify" ) );
         ssl->state++;
@@ -2831,7 +3018,8 @@
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate verify" ) );
         ssl->state++;
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 1bda53c..9afd399 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -232,7 +232,8 @@
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 &&
           MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED */
 
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
 static int ssl_parse_supported_elliptic_curves( mbedtls_ssl_context *ssl,
                                                 const unsigned char *buf,
                                                 size_t len )
@@ -305,7 +306,12 @@
         if( p[0] == MBEDTLS_ECP_PF_UNCOMPRESSED ||
             p[0] == MBEDTLS_ECP_PF_COMPRESSED )
         {
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
             ssl->handshake->ecdh_ctx.point_format = p[0];
+#endif
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+            ssl->handshake->ecjpake_ctx.point_format = p[0];
+#endif
             MBEDTLS_SSL_DEBUG_MSG( 4, ( "point format selected: %d", p[0] ) );
             return( 0 );
         }
@@ -316,7 +322,35 @@
 
     return( 0 );
 }
-#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C */
+#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C ||
+          MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+static int ssl_parse_ecjpake_kkpp( mbedtls_ssl_context *ssl,
+                                   const unsigned char *buf,
+                                   size_t len )
+{
+    int ret;
+
+    if( mbedtls_ecjpake_check( &ssl->handshake->ecjpake_ctx ) != 0 )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 3, ( "skip ecjpake kkpp extension" ) );
+        return( 0 );
+    }
+
+    if( ( ret = mbedtls_ecjpake_read_round_one( &ssl->handshake->ecjpake_ctx,
+                                                buf, len ) ) != 0 )
+    {
+        MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecjpake_read_round_one", ret );
+        return( ret );
+    }
+
+    /* Only mark the extension as OK when we're sure it is */
+    ssl->handshake->cli_exts |= MBEDTLS_TLS_EXT_ECJPAKE_KKPP_OK;
+
+    return( 0 );
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
 static int ssl_parse_max_fragment_length_ext( mbedtls_ssl_context *ssl,
@@ -707,6 +741,17 @@
     }
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    if( suite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE &&
+        ( ssl->handshake->cli_exts & MBEDTLS_TLS_EXT_ECJPAKE_KKPP_OK ) == 0 )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 3, ( "ciphersuite mismatch: ecjpake "
+                                    "not configured or ext missing" ) );
+        return( 0 );
+    }
+#endif
+
+
 #if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
     if( mbedtls_ssl_ciphersuite_uses_ec( suite_info ) &&
         ( ssl->handshake->curves == NULL ||
@@ -1541,7 +1586,8 @@
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 &&
           MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED */
 
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
         case MBEDTLS_TLS_EXT_SUPPORTED_ELLIPTIC_CURVES:
             MBEDTLS_SSL_DEBUG_MSG( 3, ( "found supported elliptic curves extension" ) );
 
@@ -1558,7 +1604,18 @@
             if( ret != 0 )
                 return( ret );
             break;
-#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C */
+#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C ||
+          MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+        case MBEDTLS_TLS_EXT_ECJPAKE_KKPP:
+            MBEDTLS_SSL_DEBUG_MSG( 3, ( "found ecjpake kkpp extension" ) );
+
+            ret = ssl_parse_ecjpake_kkpp( ssl, ext + 4, ext_size );
+            if( ret != 0 )
+                return( ret );
+            break;
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
         case MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH:
@@ -1976,7 +2033,8 @@
 }
 #endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */
 
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
 static void ssl_write_supported_point_formats_ext( mbedtls_ssl_context *ssl,
                                                    unsigned char *buf,
                                                    size_t *olen )
@@ -2004,7 +2062,51 @@
 
     *olen = 6;
 }
-#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C */
+#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+static void ssl_write_ecjpake_kkpp_ext( mbedtls_ssl_context *ssl,
+                                        unsigned char *buf,
+                                        size_t *olen )
+{
+    int ret;
+    unsigned char *p = buf;
+    const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN;
+    size_t kkpp_len;
+
+    *olen = 0;
+
+    /* Skip costly computation if not needed */
+    if( ssl->transform_negotiate->ciphersuite_info->key_exchange !=
+        MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+        return;
+
+    MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, ecjpake kkpp extension" ) );
+
+    if( end - p < 4 )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "buffer too small" ) );
+        return;
+    }
+
+    *p++ = (unsigned char)( ( MBEDTLS_TLS_EXT_ECJPAKE_KKPP >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( MBEDTLS_TLS_EXT_ECJPAKE_KKPP      ) & 0xFF );
+
+    ret = mbedtls_ecjpake_write_round_one( &ssl->handshake->ecjpake_ctx,
+                                        p + 2, end - p - 2, &kkpp_len,
+                                        ssl->conf->f_rng, ssl->conf->p_rng );
+    if( ret != 0 )
+    {
+        MBEDTLS_SSL_DEBUG_RET( 1 , "mbedtls_ecjpake_write_round_one", ret );
+        return;
+    }
+
+    *p++ = (unsigned char)( ( kkpp_len >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( kkpp_len      ) & 0xFF );
+
+    *olen = kkpp_len + 4;
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_ALPN )
 static void ssl_write_alpn_ext( mbedtls_ssl_context *ssl,
@@ -2290,11 +2392,17 @@
     ext_len += olen;
 #endif
 
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
     ssl_write_supported_point_formats_ext( ssl, p + 2 + ext_len, &olen );
     ext_len += olen;
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    ssl_write_ecjpake_kkpp_ext( ssl, p + 2 + ext_len, &olen );
+    ext_len += olen;
+#endif
+
 #if defined(MBEDTLS_SSL_ALPN)
     ssl_write_alpn_ext( ssl, p + 2 + ext_len, &olen );
     ext_len += olen;
@@ -2333,7 +2441,8 @@
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate request" ) );
         ssl->state++;
@@ -2370,6 +2479,7 @@
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE ||
         authmode == MBEDTLS_SSL_VERIFY_NONE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate request" ) );
@@ -2544,12 +2654,14 @@
     defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) ||                       \
     defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) ||                     \
     defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) ||                     \
-    defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
+    defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) ||                   \
+    defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
     unsigned char *p = ssl->out_msg + 4;
     unsigned char *dig_signed = p;
     size_t dig_signed_len = 0, len;
     ((void) dig_signed);
     ((void) dig_signed_len);
+    ((void) len);
 #endif
 
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write server key exchange" ) );
@@ -2580,6 +2692,25 @@
     }
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+    {
+        size_t jlen;
+        const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN;
+
+        ret = mbedtls_ecjpake_write_round_two( &ssl->handshake->ecjpake_ctx,
+                p, end - p, &jlen, ssl->conf->f_rng, ssl->conf->p_rng );
+        if( ret != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecjpake_write_round_two", ret );
+            return( ret );
+        }
+
+        p += jlen;
+        n += jlen;
+    }
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
 #if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) ||                       \
     defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED)
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
@@ -3331,6 +3462,28 @@
     }
     else
 #endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+    {
+        ret = mbedtls_ecjpake_read_round_two( &ssl->handshake->ecjpake_ctx,
+                                              p, end - p );
+        if( ret != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecjpake_read_round_two", ret );
+            return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE );
+        }
+
+        ret = mbedtls_ecjpake_derive_secret( &ssl->handshake->ecjpake_ctx,
+                ssl->handshake->premaster, 32, &ssl->handshake->pmslen,
+                ssl->conf->f_rng, ssl->conf->p_rng );
+        if( ret != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecjpake_derive_secret", ret );
+            return( ret );
+        }
+    }
+    else
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
     {
         MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
         return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
@@ -3362,7 +3515,8 @@
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) );
         ssl->state++;
@@ -3392,6 +3546,7 @@
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE ||
         ssl->session_negotiate->peer_cert == NULL )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) );
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index da9f8bf..eeac1bb 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -862,6 +862,16 @@
     }
 #endif /* MBEDTLS_SSL_HW_RECORD_ACCEL */
 
+#if defined(MBEDTLS_SSL_EXPORT_KEYS)
+    if( ssl->conf->f_export_keys != NULL )
+    {
+        ssl->conf->f_export_keys( ssl->conf->p_export_keys,
+                                  session->master, keyblk,
+                                  transform->maclen, transform->keylen,
+                                  iv_copy_len );
+    }
+#endif
+
     if( ( ret = mbedtls_cipher_setup( &transform->cipher_ctx_enc,
                                  cipher_info ) ) != 0 )
     {
@@ -3968,7 +3978,8 @@
 
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate" ) );
         ssl->state++;
@@ -3987,7 +3998,8 @@
 
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) );
         ssl->state++;
@@ -4009,7 +4021,8 @@
 
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate" ) );
         ssl->state++;
@@ -4124,7 +4137,8 @@
 
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
         ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) );
         ssl->state++;
@@ -5146,6 +5160,13 @@
 #if defined(MBEDTLS_ECDH_C)
     mbedtls_ecdh_init( &handshake->ecdh_ctx );
 #endif
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    mbedtls_ecjpake_init( &handshake->ecjpake_ctx );
+#if defined(MBEDTLS_SSL_CLI_C)
+    handshake->ecjpake_cache = NULL;
+    handshake->ecjpake_cache_len = 0;
+#endif
+#endif
 
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
     handshake->sni_authmode = MBEDTLS_SSL_VERIFY_UNSET;
@@ -5679,6 +5700,32 @@
 }
 #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+/*
+ * Set EC J-PAKE password for current handshake
+ */
+int mbedtls_ssl_set_hs_ecjpake_password( mbedtls_ssl_context *ssl,
+                                         const unsigned char *pw,
+                                         size_t pw_len )
+{
+    mbedtls_ecjpake_role role;
+
+    if( ssl->handshake == NULL && ssl->conf == NULL )
+        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+    if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
+        role = MBEDTLS_ECJPAKE_SERVER;
+    else
+        role = MBEDTLS_ECJPAKE_CLIENT;
+
+    return( mbedtls_ecjpake_setup( &ssl->handshake->ecjpake_ctx,
+                                   role,
+                                   MBEDTLS_MD_SHA256,
+                                   MBEDTLS_ECP_DP_SECP256R1,
+                                   pw, pw_len ) );
+}
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
 int mbedtls_ssl_conf_psk( mbedtls_ssl_config *conf,
                 const unsigned char *psk, size_t psk_len,
@@ -6002,6 +6049,16 @@
 #endif
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
 
+#if defined(MBEDTLS_SSL_EXPORT_KEYS)
+void mbedtls_ssl_conf_export_keys_cb( mbedtls_ssl_config *conf,
+        mbedtls_ssl_export_keys_t *f_export_keys,
+        void *p_export_keys )
+{
+    conf->f_export_keys = f_export_keys;
+    conf->p_export_keys = p_export_keys;
+}
+#endif
+
 /*
  * SSL get accessors
  */
@@ -6822,6 +6879,14 @@
 #if defined(MBEDTLS_ECDH_C)
     mbedtls_ecdh_free( &handshake->ecdh_ctx );
 #endif
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    mbedtls_ecjpake_free( &handshake->ecjpake_ctx );
+#if defined(MBEDTLS_SSL_CLI_C)
+    mbedtls_free( handshake->ecjpake_cache );
+    handshake->ecjpake_cache = NULL;
+    handshake->ecjpake_cache_len = 0;
+#endif
+#endif
 
 #if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)
     /* explicit void pointer cast for buggy MS compiler */
@@ -7171,7 +7236,8 @@
     mbedtls_zeroize( conf, sizeof( mbedtls_ssl_config ) );
 }
 
-#if defined(MBEDTLS_PK_C)
+#if defined(MBEDTLS_PK_C) && \
+    ( defined(MBEDTLS_RSA_C) || defined(MBEDTLS_ECDSA_C) )
 /*
  * Convert between MBEDTLS_PK_XXX and SSL_SIG_XXX
  */
@@ -7204,7 +7270,7 @@
             return( MBEDTLS_PK_NONE );
     }
 }
-#endif /* MBEDTLS_PK_C */
+#endif /* MBEDTLS_PK_C && ( MBEDTLS_RSA_C || MBEDTLS_ECDSA_C ) */
 
 /*
  * Convert from MBEDTLS_SSL_HASH_XXX to MBEDTLS_MD_XXX
@@ -7360,6 +7426,7 @@
             case MBEDTLS_KEY_EXCHANGE_PSK:
             case MBEDTLS_KEY_EXCHANGE_DHE_PSK:
             case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK:
+            case MBEDTLS_KEY_EXCHANGE_ECJPAKE:
                 usage = 0;
         }
     }
diff --git a/library/version_features.c b/library/version_features.c
index 196b93c..1575e09 100644
--- a/library/version_features.c
+++ b/library/version_features.c
@@ -264,6 +264,9 @@
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED)
     "MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED",
 #endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED */
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED",
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 #if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
     "MBEDTLS_PK_PARSE_EC_EXTENDED",
 #endif /* MBEDTLS_PK_PARSE_EC_EXTENDED */
@@ -378,6 +381,9 @@
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
     "MBEDTLS_SSL_SESSION_TICKETS",
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
+#if defined(MBEDTLS_SSL_EXPORT_KEYS)
+    "MBEDTLS_SSL_EXPORT_KEYS",
+#endif /* MBEDTLS_SSL_EXPORT_KEYS */
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
     "MBEDTLS_SSL_SERVER_NAME_INDICATION",
 #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
@@ -465,6 +471,9 @@
 #if defined(MBEDTLS_ECDSA_C)
     "MBEDTLS_ECDSA_C",
 #endif /* MBEDTLS_ECDSA_C */
+#if defined(MBEDTLS_ECJPAKE_C)
+    "MBEDTLS_ECJPAKE_C",
+#endif /* MBEDTLS_ECJPAKE_C */
 #if defined(MBEDTLS_ECP_C)
     "MBEDTLS_ECP_C",
 #endif /* MBEDTLS_ECP_C */
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index c090db9..559e502 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -75,6 +75,7 @@
 #define DFL_KEY_FILE            ""
 #define DFL_PSK                 ""
 #define DFL_PSK_IDENTITY        "Client_identity"
+#define DFL_ECJPAKE_PW          NULL
 #define DFL_FORCE_CIPHER        0
 #define DFL_RENEGOTIATION       MBEDTLS_SSL_RENEGOTIATION_DISABLED
 #define DFL_ALLOW_LEGACY        -2
@@ -211,6 +212,13 @@
 #define USAGE_RENEGO ""
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+#define USAGE_ECJPAKE \
+    "    ecjpake_pw=%%s       default: none (disabled)\n"
+#else
+#define USAGE_ECJPAKE ""
+#endif
+
 #define USAGE \
     "\n usage: ssl_client2 param=<>...\n"                   \
     "\n acceptable parameters:\n"                           \
@@ -233,6 +241,7 @@
     USAGE_IO                                                \
     "\n"                                                    \
     USAGE_PSK                                               \
+    USAGE_ECJPAKE                                           \
     "\n"                                                    \
     "    allow_legacy=%%d     default: (library default: no)\n"      \
     USAGE_RENEGO                                            \
@@ -279,6 +288,7 @@
     const char *key_file;       /* the file with the client key             */
     const char *psk;            /* the pre-shared key                       */
     const char *psk_identity;   /* the pre-shared key identity              */
+    const char *ecjpake_pw;     /* the EC J-PAKE password                   */
     int force_ciphersuite[2];   /* protocol/ciphersuite to use, or all      */
     int renegotiation;          /* enable / disable renegotiation           */
     int allow_legacy;           /* allow legacy renegotiation               */
@@ -469,6 +479,7 @@
     opt.key_file            = DFL_KEY_FILE;
     opt.psk                 = DFL_PSK;
     opt.psk_identity        = DFL_PSK_IDENTITY;
+    opt.ecjpake_pw          = DFL_ECJPAKE_PW;
     opt.force_ciphersuite[0]= DFL_FORCE_CIPHER;
     opt.renegotiation       = DFL_RENEGOTIATION;
     opt.allow_legacy        = DFL_ALLOW_LEGACY;
@@ -557,6 +568,8 @@
             opt.psk = q;
         else if( strcmp( p, "psk_identity" ) == 0 )
             opt.psk_identity = q;
+        else if( strcmp( p, "ecjpake_pw" ) == 0 )
+            opt.ecjpake_pw = q;
         else if( strcmp( p, "force_ciphersuite" ) == 0 )
         {
             opt.force_ciphersuite[0] = mbedtls_ssl_get_ciphersuite_id( q );
@@ -1204,6 +1217,19 @@
     }
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    if( opt.ecjpake_pw != DFL_ECJPAKE_PW )
+    {
+        if( ( ret = mbedtls_ssl_set_hs_ecjpake_password( &ssl,
+                        (const unsigned char *) opt.ecjpake_pw,
+                                        strlen( opt.ecjpake_pw ) ) ) != 0 )
+        {
+            mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n", ret );
+            goto exit;
+        }
+    }
+#endif
+
     if( opt.nbio == 2 )
         mbedtls_ssl_set_bio( &ssl, &server_fd, my_send, my_recv, NULL );
     else
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index bd4d1d1..b586a70 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -102,6 +102,7 @@
 #define DFL_KEY_FILE2           ""
 #define DFL_PSK                 ""
 #define DFL_PSK_IDENTITY        "Client_identity"
+#define DFL_ECJPAKE_PW          NULL
 #define DFL_PSK_LIST            NULL
 #define DFL_FORCE_CIPHER        0
 #define DFL_VERSION_SUITES      NULL
@@ -293,6 +294,13 @@
 #define USAGE_RENEGO ""
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+#define USAGE_ECJPAKE \
+    "    ecjpake_pw=%%s       default: none (disabled)\n"
+#else
+#define USAGE_ECJPAKE ""
+#endif
+
 #define USAGE \
     "\n usage: ssl_server2 param=<>...\n"                   \
     "\n acceptable parameters:\n"                           \
@@ -314,6 +322,7 @@
     USAGE_SNI                                               \
     "\n"                                                    \
     USAGE_PSK                                               \
+    USAGE_ECJPAKE                                           \
     "\n"                                                    \
     "    allow_legacy=%%d     default: (library default: no)\n"      \
     USAGE_RENEGO                                            \
@@ -358,6 +367,7 @@
     const char *psk;            /* the pre-shared key                       */
     const char *psk_identity;   /* the pre-shared key identity              */
     char *psk_list;             /* list of PSK id/key pairs for callback    */
+    const char *ecjpake_pw;     /* the EC J-PAKE password                   */
     int force_ciphersuite[2];   /* protocol/ciphersuite to use, or all      */
     const char *version_suites; /* per-version ciphersuites                 */
     int renegotiation;          /* enable / disable renegotiation           */
@@ -900,6 +910,7 @@
     opt.psk                 = DFL_PSK;
     opt.psk_identity        = DFL_PSK_IDENTITY;
     opt.psk_list            = DFL_PSK_LIST;
+    opt.ecjpake_pw          = DFL_ECJPAKE_PW;
     opt.force_ciphersuite[0]= DFL_FORCE_CIPHER;
     opt.version_suites      = DFL_VERSION_SUITES;
     opt.renegotiation       = DFL_RENEGOTIATION;
@@ -985,6 +996,8 @@
             opt.psk_identity = q;
         else if( strcmp( p, "psk_list" ) == 0 )
             opt.psk_list = q;
+        else if( strcmp( p, "ecjpake_pw" ) == 0 )
+            opt.ecjpake_pw = q;
         else if( strcmp( p, "force_ciphersuite" ) == 0 )
         {
             opt.force_ciphersuite[0] = mbedtls_ssl_get_ciphersuite_id( q );
@@ -1904,6 +1917,19 @@
     }
 #endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY */
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+    if( opt.ecjpake_pw != DFL_ECJPAKE_PW )
+    {
+        if( ( ret = mbedtls_ssl_set_hs_ecjpake_password( &ssl,
+                        (const unsigned char *) opt.ecjpake_pw,
+                                        strlen( opt.ecjpake_pw ) ) ) != 0 )
+        {
+            mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n", ret );
+            goto exit;
+        }
+    }
+#endif
+
     mbedtls_printf( " ok\n" );
 
     /*
diff --git a/programs/test/selftest.c b/programs/test/selftest.c
index 82e9581..fe5d514 100644
--- a/programs/test/selftest.c
+++ b/programs/test/selftest.c
@@ -49,6 +49,7 @@
 #include "mbedtls/xtea.h"
 #include "mbedtls/pkcs5.h"
 #include "mbedtls/ecp.h"
+#include "mbedtls/ecjpake.h"
 #include "mbedtls/timing.h"
 
 #include <stdio.h>
@@ -244,6 +245,11 @@
         return( ret );
 #endif
 
+#if defined(MBEDTLS_ECJPAKE_C)
+    if( ( ret = mbedtls_ecjpake_self_test( v ) ) != 0 )
+        return( ret );
+#endif
+
 #if defined(MBEDTLS_DHM_C)
     if( ( ret = mbedtls_dhm_self_test( v ) ) != 0 )
         return( ret );
diff --git a/scripts/footprint.sh b/scripts/footprint.sh
new file mode 100755
index 0000000..a11844f
--- /dev/null
+++ b/scripts/footprint.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+set -eu
+
+CONFIG_H='include/mbedtls/config.h'
+
+if [ -r $CONFIG_H ]; then :; else
+    echo "$CONFIG_H not found" >&2
+    exit 1
+fi
+
+if grep -i cmake Makefile >/dev/null; then
+    echo "Not compatible with CMake" >&2
+    exit 1
+fi
+
+doit()
+{
+    NAME="$1"
+    FILE="$2"
+
+    echo "$NAME:"
+
+    cp $CONFIG_H ${CONFIG_H}.bak
+    cp "$FILE" include/mbedtls/config.h
+
+    {
+        scripts/config.pl unset MBEDTLS_NET_C || true
+        scripts/config.pl unset MBEDTLS_TIMING_C || true
+        scripts/config.pl unset MBEDTLS_FS_IO || true
+    } >/dev/null 2>&1
+
+    CC=arm-none-eabi-gcc AR=arm-none-eabi-ar LD=arm-none-eabi-ld \
+        CFLAGS='-Wa,--noexecstack -Os -march=armv7-m -mthumb' \
+        make clean lib >/dev/null
+
+    OUT="size-${NAME}.txt"
+    arm-none-eabi-size -t library/libmbed*.a > "$OUT"
+    head -n1 "$OUT"
+    tail -n1 "$OUT"
+
+    cp ${CONFIG_H}.bak $CONFIG_H
+}
+
+# creates the yotta config
+yotta/create-module.sh >/dev/null
+
+doit default    include/mbedtls/config.h.bak
+doit yotta      yotta/module/mbedtls/config.h
+doit thread     configs/config-thread.h
+doit ecc        configs/config-suite-b.h
+doit psk        configs/config-ccm-psk-tls1_2.h
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 864ea77..1cca818 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -60,9 +60,10 @@
 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(ecjpake)
+add_test_suite(ecp)
 add_test_suite(entropy)
 add_test_suite(error)
 add_test_suite(gcm gcm.aes128_en)
diff --git a/tests/Makefile b/tests/Makefile
index e97887a..c5172e4 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -60,7 +60,7 @@
 	test_suite_ctr_drbg$(EXEXT)	test_suite_debug$(EXEXT)	\
 	test_suite_des$(EXEXT)		test_suite_dhm$(EXEXT)		\
 	test_suite_ecdh$(EXEXT)		test_suite_ecdsa$(EXEXT)	\
-	test_suite_ecp$(EXEXT)						\
+	test_suite_ecjpake$(EXEXT)	test_suite_ecp$(EXEXT)		\
 	test_suite_error$(EXEXT)	test_suite_entropy$(EXEXT)	\
 	test_suite_gcm.aes128_de$(EXEXT)				\
 	test_suite_gcm.aes192_de$(EXEXT)				\
@@ -292,6 +292,10 @@
 	echo "  CC    $<"
 	$(CC) $(LOCAL_CFLAGS) $(CFLAGS) $<	$(LOCAL_LDFLAGS) $(LDFLAGS) -o $@
 
+test_suite_ecjpake$(EXEXT): test_suite_ecjpake.c $(DEP)
+	echo "  CC    $<"
+	$(CC) $(LOCAL_CFLAGS) $(CFLAGS) $<	$(LOCAL_LDFLAGS) $(LDFLAGS) -o $@
+
 test_suite_ecp$(EXEXT): test_suite_ecp.c $(DEP)
 	echo "  CC    $<"
 	$(CC) $(LOCAL_CFLAGS) $(CFLAGS) $<	$(LOCAL_LDFLAGS) $(LDFLAGS) -o $@
diff --git a/tests/scripts/curves.pl b/tests/scripts/curves.pl
index 545b3b7..654bc5c 100755
--- a/tests/scripts/curves.pl
+++ b/tests/scripts/curves.pl
@@ -23,6 +23,8 @@
 
 for my $curve (@curves) {
     system( "cp $config_h.bak $config_h" ) and die "$config_h not restored\n";
+    # depends on a specific curve. Also, ignore error if it wasn't enabled
+    system( "scripts/config.pl unset MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED" );
     system( "make clean" ) and die;
 
     print "\n******************************************\n";
@@ -32,7 +34,7 @@
     system( "scripts/config.pl unset $curve" )
         and abort "Failed to disable $curve\n";
 
-    system( "make mbedtls" ) and abort "Failed to build lib: $curve\n";
+    system( "make lib" ) and abort "Failed to build lib: $curve\n";
     system( "cd tests && make" ) and abort "Failed to build tests: $curve\n";
     system( "make test" ) and abort "Failed test suite: $curve\n";
 
diff --git a/tests/scripts/test-ref-configs.pl b/tests/scripts/test-ref-configs.pl
index d1cc924..b27d64c 100755
--- a/tests/scripts/test-ref-configs.pl
+++ b/tests/scripts/test-ref-configs.pl
@@ -11,14 +11,20 @@
 use strict;
 
 my %configs = (
-    'config-mini-tls1_1.h'
-        => '-m tls1_1 -f \'^DES-CBC3-SHA$\|^TLS-RSA-WITH-3DES-EDE-CBC-SHA$\'',
-    'config-suite-b.h'
-        => "-m tls1_2 -f 'ECDHE-ECDSA.*AES.*GCM' -p mbedTLS",
-    'config-picocoin.h'
-        => 0,
-    'config-ccm-psk-tls1_2.h'
-        => '-m tls1_2 -f \'^TLS-PSK-WITH-AES-...-CCM-8\'',
+    'config-mini-tls1_1.h' => {
+        'compat' => '-m tls1_1 -f \'^DES-CBC3-SHA$\|^TLS-RSA-WITH-3DES-EDE-CBC-SHA$\'',
+    },
+    'config-suite-b.h' => {
+        'compat' => "-m tls1_2 -f 'ECDHE-ECDSA.*AES.*GCM' -p mbedTLS",
+    },
+    'config-picocoin.h' => {
+    },
+    'config-ccm-psk-tls1_2.h' => {
+        'compat' => '-m tls1_2 -f \'^TLS-PSK-WITH-AES-...-CCM-8\'',
+    },
+    'config-thread.h' => {
+        'opt' => '-f ECJPAKE.*nolog',
+    },
 );
 
 # If no config-name is provided, use all known configs.
@@ -46,7 +52,7 @@
     die $_[0];
 }
 
-while( my ($conf, $args) = each %configs ) {
+while( my ($conf, $data) = each %configs ) {
     system( "cp $config_h.bak $config_h" ) and die;
     system( "make clean" ) and die;
 
@@ -60,16 +66,29 @@
     system( "make" ) and abort "Failed to build: $conf\n";
     system( "make test" ) and abort "Failed test suite: $conf\n";
 
-    if( $args )
+    my $compat = $data->{'compat'};
+    if( $compat )
     {
-        print "\nrunning compat.sh $args\n";
-        system( "tests/compat.sh $args" )
+        print "\nrunning compat.sh $compat\n";
+        system( "tests/compat.sh $compat" )
             and abort "Failed compat.sh: $conf\n";
     }
     else
     {
         print "\nskipping compat.sh\n";
     }
+
+    my $opt = $data->{'opt'};
+    if( $opt )
+    {
+        print "\nrunning ssl-opt.sh $opt\n";
+        system( "tests/ssl-opt.sh $opt" )
+            and abort "Failed ssl-opt.sh: $conf\n";
+    }
+    else
+    {
+        print "\nskipping ssl-opt.sh\n";
+    }
 }
 
 system( "mv $config_h.bak $config_h" ) and warn "$config_h not restored\n";
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index e494413..c0b6f94 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -2499,6 +2499,98 @@
             -S "SSL - Unknown identity received" \
             -s "SSL - Verification of the message MAC failed"
 
+# Tests for EC J-PAKE
+
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE
+run_test    "ECJPAKE: client not configured" \
+            "$P_SRV debug_level=3" \
+            "$P_CLI debug_level=3" \
+            0 \
+            -C "add ciphersuite: c0ff" \
+            -C "adding ecjpake_kkpp extension" \
+            -S "found ecjpake kkpp extension" \
+            -S "skip ecjpake kkpp extension" \
+            -S "ciphersuite mismatch: ecjpake not configured" \
+            -S "server hello, ecjpake kkpp extension" \
+            -C "found ecjpake_kkpp extension" \
+            -S "None of the common ciphersuites is usable"
+
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE
+run_test    "ECJPAKE: server not configured" \
+            "$P_SRV debug_level=3" \
+            "$P_CLI debug_level=3 ecjpake_pw=bla \
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            1 \
+            -c "add ciphersuite: c0ff" \
+            -c "adding ecjpake_kkpp extension" \
+            -s "found ecjpake kkpp extension" \
+            -s "skip ecjpake kkpp extension" \
+            -s "ciphersuite mismatch: ecjpake not configured" \
+            -S "server hello, ecjpake kkpp extension" \
+            -C "found ecjpake_kkpp extension" \
+            -s "None of the common ciphersuites is usable"
+
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE
+run_test    "ECJPAKE: working, TLS" \
+            "$P_SRV debug_level=3 ecjpake_pw=bla" \
+            "$P_CLI debug_level=3 ecjpake_pw=bla \
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            0 \
+            -c "add ciphersuite: c0ff" \
+            -c "adding ecjpake_kkpp extension" \
+            -C "re-using cached ecjpake parameters" \
+            -s "found ecjpake kkpp extension" \
+            -S "skip ecjpake kkpp extension" \
+            -S "ciphersuite mismatch: ecjpake not configured" \
+            -s "server hello, ecjpake kkpp extension" \
+            -c "found ecjpake_kkpp extension" \
+            -S "None of the common ciphersuites is usable" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE
+run_test    "ECJPAKE: password mismatch, TLS" \
+            "$P_SRV debug_level=3 ecjpake_pw=bla" \
+            "$P_CLI debug_level=3 ecjpake_pw=bad \
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            1 \
+            -C "re-using cached ecjpake parameters" \
+            -s "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE
+run_test    "ECJPAKE: working, DTLS" \
+            "$P_SRV debug_level=3 dtls=1 ecjpake_pw=bla" \
+            "$P_CLI debug_level=3 dtls=1 ecjpake_pw=bla \
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            0 \
+            -c "re-using cached ecjpake parameters" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE
+run_test    "ECJPAKE: working, DTLS, no cookie" \
+            "$P_SRV debug_level=3 dtls=1 ecjpake_pw=bla cookies=0" \
+            "$P_CLI debug_level=3 dtls=1 ecjpake_pw=bla \
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            0 \
+            -C "re-using cached ecjpake parameters" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE
+run_test    "ECJPAKE: password mismatch, DTLS" \
+            "$P_SRV debug_level=3 dtls=1 ecjpake_pw=bla" \
+            "$P_CLI debug_level=3 dtls=1 ecjpake_pw=bad \
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            1 \
+            -c "re-using cached ecjpake parameters" \
+            -s "SSL - Verification of the message MAC failed"
+
+# for tests with configs/config-thread.h
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE
+run_test    "ECJPAKE: working, DTLS, nolog" \
+            "$P_SRV dtls=1 ecjpake_pw=bla" \
+            "$P_CLI dtls=1 ecjpake_pw=bla \
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            0
+
 # Tests for ciphersuites per version
 
 run_test    "Per-version suites: SSL3" \
diff --git a/tests/suites/test_suite_ecjpake.data b/tests/suites/test_suite_ecjpake.data
new file mode 100644
index 0000000..1a772a9
--- /dev/null
+++ b/tests/suites/test_suite_ecjpake.data
@@ -0,0 +1,230 @@
+ECJPAKE selftest
+ecjpake_selftest:
+
+ECJPAKE round one: client, valid
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51620934d74eb43e54df424fd96306c0117bf131afabf90a9d33d1198d905193735144104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb12":0
+
+ECJPAKE round one: server, valid
+read_round_one:MBEDTLS_ECJPAKE_SERVER:"4104accf0106ef858fa2d919331346805a78b58bbad0b844e5c7892879146187dd2666ada781bb7f111372251a8910621f634df128ac48e381fd6ef9060731f694a441041dd0bd5d4566c9bed9ce7de701b5e82e08e84b730466018ab903c79eb982172236c0c1728ae4bf73610d34de44246ef3d9c05a2236fb66a6583d7449308babce2072fe16662992e9235c25002f11b15087b82738e03c945bf7a2995dda1e98345841047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b4104a49558d32ed1ebfc1816af4ff09b55fcb4ca47b2a02d1e7caf1179ea3fe1395b22b861964016fabaf72c975695d93d4df0e5197fe9f040634ed59764937787be20bc4deebbf9b8d60a335f046ca3aa941e45864c7cadef9cf75b3d8b010e443ef0":0
+
+ECJPAKE round one: role mismatch
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104accf0106ef858fa2d919331346805a78b58bbad0b844e5c7892879146187dd2666ada781bb7f111372251a8910621f634df128ac48e381fd6ef9060731f694a441041dd0bd5d4566c9bed9ce7de701b5e82e08e84b730466018ab903c79eb982172236c0c1728ae4bf73610d34de44246ef3d9c05a2236fb66a6583d7449308babce2072fe16662992e9235c25002f11b15087b82738e03c945bf7a2995dda1e98345841047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b4104a49558d32ed1ebfc1816af4ff09b55fcb4ca47b2a02d1e7caf1179ea3fe1395b22b861964016fabaf72c975695d93d4df0e5197fe9f040634ed59764937787be20bc4deebbf9b8d60a335f046ca3aa941e45864c7cadef9cf75b3d8b010e443ef0":MBEDTLS_ERR_ECP_VERIFY_FAILED
+
+ECJPAKE round one: trailing byte
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51620934d74eb43e54df424fd96306c0117bf131afabf90a9d33d1198d905193735144104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1200":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: no data
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: length of first point too small
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: length of first point too big
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"01":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: no point data
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"0104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: first point is zero
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"0100":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round one: KKP1: unknown first point format
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41057ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECJPAKE round one: KKP1: nothing after first point
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: length of second point too small
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: length of second point too big
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b01":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: no second point data
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b0104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: unknow second point format
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410509f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECJPAKE round one: KKP1: nothing after second point
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: zero-length r
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51600":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round one: KKP1: no data for r
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51601":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP1: corrupted r
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51620934d74eb43e54df424fd96306c0117bf131afabf90a9d33d1198d90519373515":MBEDTLS_ERR_ECP_VERIFY_FAILED
+
+ECJPAKE round one: KKP1: X not on the curve
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"41047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2a410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51620934d74eb43e54df424fd96306c0117bf131afabf90a9d33d1198d90519373514":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round one: KKP2: no data
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb12":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: length of first point too small
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1200":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: length of first point too big
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1201":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: no point data
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb120104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: first point is zero
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb120100":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round one: KKP2: unknown first point format
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241057ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECJPAKE round one: KKP2: nothing after first point
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: length of second point too small
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: length of second point too big
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b01":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: no second point data
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b0104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: unknow second point format
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410509f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECJPAKE round one: KKP2: nothing after second point
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb516":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: zero-length r
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51600":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round one: KKP2: no data for r
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51601":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round one: KKP2: corrupted r
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2b410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51620934d74eb43e54df424fd96306c0117bf131afabf90a9d33d1198d90519373515":MBEDTLS_ERR_ECP_VERIFY_FAILED
+
+ECJPAKE round one: KKP2: X not on the curve
+read_round_one:MBEDTLS_ECJPAKE_CLIENT:"4104190a07700ffa4be6ae1d79ee0f06aeb544cd5addaabedf70f8623321332c54f355f0fbfec783ed359e5d0bf7377a0fc4ea7ace473c9c112b41ccd41ac56a56124104360a1cea33fce641156458e0a4eac219e96831e6aebc88b3f3752f93a0281d1bf1fb106051db9694a8d6e862a5ef1324a3d9e27894f1ee4f7c59199965a8dd4a2091847d2d22df3ee55faa2a3fb33fd2d1e055a07a7c61ecfb8d80ec00c2c9eb1241047ea6e3a4487037a9e0dbd79262b2cc273e779930fc18409ac5361c5fe669d702e147790aeb4ce7fd6575ab0f6c7fd1c335939aa863ba37ec91b7e32bb013bb2a410409f85b3d20ebd7885ce464c08d056d6428fe4dd9287aa365f131f4360ff386d846898bc4b41583c2a5197f65d78742746c12a5ec0a4ffe2f270a750a1d8fb51620934d74eb43e54df424fd96306c0117bf131afabf90a9d33d1198d90519373514":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round two client: valid
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641045516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c820ff724a9a70b88cb86f20b434c6865aa1cd7906dd7c9bce3525f508276f26836c":0
+
+ECJPAKE round two client: trailing byte
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641045516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c820ff724a9a70b88cb86f20b434c6865aa1cd7906dd7c9bce3525f508276f26836c00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: no data
+read_round_two_cli:"":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: ECParams too short
+read_round_two_cli:"0300":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: ECParams not named curve
+read_round_two_cli:"010017":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: ECParams wrong curve
+read_round_two_cli:"030016":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECJPAKE round two client: no data after ECParams
+read_round_two_cli:"030017":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: length of first point too small
+read_round_two_cli:"03001700":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: length of first point too big
+read_round_two_cli:"03001701":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: no first point data
+read_round_two_cli:"0300170104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: first point is zero
+read_round_two_cli:"0300170100":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round two client: unknown first point format
+read_round_two_cli:"03001741050fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a6":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECJPAKE round two client: nothing after first point
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a6":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: length of second point too small
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a600":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: length of second point too big
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a601":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: no second point data
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a60104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: unknown second point format
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641055516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c8":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECJPAKE round two client: nothing after second point
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641045516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c8":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: zero-length r
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641045516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c800":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round two client: no data for r
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641045516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c801":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two client: corrupted r
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a641045516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c820ff724a9a70b88cb86f20b434c6865aa1cd7906dd7c9bce3525f508276f26836d":MBEDTLS_ERR_ECP_VERIFY_FAILED
+
+ECJPAKE round two client: X not on the curve
+read_round_two_cli:"03001741040fb22b1d5d1123e0ef9feb9d8a2e590a1f4d7ced2c2b06586e8f2a16d4eb2fda4328a20b07d8fd667654ca18c54e32a333a0845451e926ee8804fd7af0aaa7a741045516ea3e54a0d5d8b2ce786b38d383370029a5dbe4459c9dd601b408a24ae6465c8ac905b9eb03b5d3691c139ef83f1cd4200f6c9cd4ec392218a59ed243d3c820ff724a9a70b88cb86f20b434c6865aa1cd7906dd7c9bce3525f508276f26836c":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round two server: valid
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d200f011f19483535a6e89a580c9b0003baf21462ece91a82cc38dbdcae60d9c54c":0
+
+ECJPAKE round two server: trailing byte
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d200f011f19483535a6e89a580c9b0003baf21462ece91a82cc38dbdcae60d9c54c00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: no data
+read_round_two_srv:"":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: length of forst point too small
+read_round_two_srv:"00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: length of first point too big
+read_round_two_srv:"01":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: no first point data
+read_round_two_srv:"0104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: first point is zero
+read_round_two_srv:"0100":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round two server: unknown first point format
+read_round_two_srv:"410569d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECJPAKE round two server: nothing after first point
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: length of second point too small
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee00":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: length of second point too big
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee01":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: no second point data
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee0104":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: unknown second point format
+read_round_two_srv:"410569d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d":MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+
+ECJPAKE round two server: nothing after second point
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: zero-length r
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d00":MBEDTLS_ERR_ECP_INVALID_KEY
+
+ECJPAKE round two server: no data for r
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d20":MBEDTLS_ERR_ECP_BAD_INPUT_DATA
+
+ECJPAKE round two server: corrupted r
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ee4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d200f011f19483535a6e89a580c9b0003baf21462ece91a82cc38dbdcae60d9c54d":MBEDTLS_ERR_ECP_VERIFY_FAILED
+
+ECJPAKE round two server: X not on curve
+read_round_two_srv:"410469d54ee85e90ce3f1246742de507e939e81d1dc1c5cb988b58c310c9fdd9524d93720b45541c83ee8841191da7ced86e3312d43623c1d63e74989aba4affd1ef4104077e8c31e20e6bedb760c13593e69f15be85c27d68cd09ccb8c4183608917c5c3d409fac39fefee82f7292d36f0d23e055913f45a52b85dd8a2052e9e129bb4d200f011f19483535a6e89a580c9b0003baf21462ece91a82cc38dbdcae60d9c54c":MBEDTLS_ERR_ECP_INVALID_KEY
diff --git a/tests/suites/test_suite_ecjpake.function b/tests/suites/test_suite_ecjpake.function
new file mode 100644
index 0000000..8d867b7
--- /dev/null
+++ b/tests/suites/test_suite_ecjpake.function
@@ -0,0 +1,190 @@
+/* BEGIN_HEADER */
+#include "mbedtls/ecjpake.h"
+
+#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) && defined(MBEDTLS_SHA256_C)
+static const unsigned char ecjpake_test_x1[] = {
+    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+    0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+    0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x21
+};
+
+static const unsigned char ecjpake_test_x2[] = {
+    0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+    0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+    0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x81
+};
+
+static const unsigned char ecjpake_test_x3[] = {
+    0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+    0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+    0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x81
+};
+
+static const unsigned char ecjpake_test_x4[] = {
+    0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc,
+    0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
+    0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe1
+};
+
+static const unsigned char ecjpake_test_X1[] = {
+    0x04, 0xac, 0xcf, 0x01, 0x06, 0xef, 0x85, 0x8f, 0xa2, 0xd9, 0x19, 0x33,
+    0x13, 0x46, 0x80, 0x5a, 0x78, 0xb5, 0x8b, 0xba, 0xd0, 0xb8, 0x44, 0xe5,
+    0xc7, 0x89, 0x28, 0x79, 0x14, 0x61, 0x87, 0xdd, 0x26, 0x66, 0xad, 0xa7,
+    0x81, 0xbb, 0x7f, 0x11, 0x13, 0x72, 0x25, 0x1a, 0x89, 0x10, 0x62, 0x1f,
+    0x63, 0x4d, 0xf1, 0x28, 0xac, 0x48, 0xe3, 0x81, 0xfd, 0x6e, 0xf9, 0x06,
+    0x07, 0x31, 0xf6, 0x94, 0xa4
+};
+
+static const unsigned char ecjpake_test_X2[] = {
+    0x04, 0x7e, 0xa6, 0xe3, 0xa4, 0x48, 0x70, 0x37, 0xa9, 0xe0, 0xdb, 0xd7,
+    0x92, 0x62, 0xb2, 0xcc, 0x27, 0x3e, 0x77, 0x99, 0x30, 0xfc, 0x18, 0x40,
+    0x9a, 0xc5, 0x36, 0x1c, 0x5f, 0xe6, 0x69, 0xd7, 0x02, 0xe1, 0x47, 0x79,
+    0x0a, 0xeb, 0x4c, 0xe7, 0xfd, 0x65, 0x75, 0xab, 0x0f, 0x6c, 0x7f, 0xd1,
+    0xc3, 0x35, 0x93, 0x9a, 0xa8, 0x63, 0xba, 0x37, 0xec, 0x91, 0xb7, 0xe3,
+    0x2b, 0xb0, 0x13, 0xbb, 0x2b
+};
+
+static const unsigned char ecjpake_test_X3[] = {
+    0x04, 0x7e, 0xa6, 0xe3, 0xa4, 0x48, 0x70, 0x37, 0xa9, 0xe0, 0xdb, 0xd7,
+    0x92, 0x62, 0xb2, 0xcc, 0x27, 0x3e, 0x77, 0x99, 0x30, 0xfc, 0x18, 0x40,
+    0x9a, 0xc5, 0x36, 0x1c, 0x5f, 0xe6, 0x69, 0xd7, 0x02, 0xe1, 0x47, 0x79,
+    0x0a, 0xeb, 0x4c, 0xe7, 0xfd, 0x65, 0x75, 0xab, 0x0f, 0x6c, 0x7f, 0xd1,
+    0xc3, 0x35, 0x93, 0x9a, 0xa8, 0x63, 0xba, 0x37, 0xec, 0x91, 0xb7, 0xe3,
+    0x2b, 0xb0, 0x13, 0xbb, 0x2b
+};
+
+static const unsigned char ecjpake_test_X4[] = {
+    0x04, 0x19, 0x0a, 0x07, 0x70, 0x0f, 0xfa, 0x4b, 0xe6, 0xae, 0x1d, 0x79,
+    0xee, 0x0f, 0x06, 0xae, 0xb5, 0x44, 0xcd, 0x5a, 0xdd, 0xaa, 0xbe, 0xdf,
+    0x70, 0xf8, 0x62, 0x33, 0x21, 0x33, 0x2c, 0x54, 0xf3, 0x55, 0xf0, 0xfb,
+    0xfe, 0xc7, 0x83, 0xed, 0x35, 0x9e, 0x5d, 0x0b, 0xf7, 0x37, 0x7a, 0x0f,
+    0xc4, 0xea, 0x7a, 0xce, 0x47, 0x3c, 0x9c, 0x11, 0x2b, 0x41, 0xcc, 0xd4,
+    0x1a, 0xc5, 0x6a, 0x56, 0x12
+};
+
+/* Load my private and public keys, and peer's public keys */
+static int ecjpake_test_load( mbedtls_ecjpake_context *ctx,
+                              const unsigned char *xm1, size_t len_xm1,
+                              const unsigned char *xm2, size_t len_xm2,
+                              const unsigned char *Xm1, size_t len_Xm1,
+                              const unsigned char *Xm2, size_t len_Xm2,
+                              const unsigned char *Xp1, size_t len_Xp1,
+                              const unsigned char *Xp2, size_t len_Xp2 )
+{
+    int ret;
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &ctx->xm1, xm1, len_xm1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &ctx->xm2, xm2, len_xm2 ) );
+
+    MBEDTLS_MPI_CHK( mbedtls_ecp_point_read_binary( &ctx->grp,
+                     &ctx->Xm1, Xm1, len_Xm1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_point_read_binary( &ctx->grp,
+                     &ctx->Xm2, Xm2, len_Xm2 ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_point_read_binary( &ctx->grp,
+                     &ctx->Xp1, Xp1, len_Xp1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_ecp_point_read_binary( &ctx->grp,
+                     &ctx->Xp2, Xp2, len_Xp2 ) );
+
+cleanup:
+    return( ret );
+}
+
+#define ADD_SIZE( x )   x, sizeof( x )
+#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED && MBEDTLS_SHA256_C */
+/* END_HEADER */
+
+/* BEGIN_DEPENDENCIES
+ * depends_on:MBEDTLS_ECJPAKE_C
+ * END_DEPENDENCIES
+ */
+
+/* BEGIN_CASE depends_on:MBEDTLS_SELF_TEST */
+void ecjpake_selftest()
+{
+    TEST_ASSERT( mbedtls_ecjpake_self_test( 0 ) == 0 );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C */
+void read_round_one( int role, char *data, int ref_ret )
+{
+    mbedtls_ecjpake_context ctx;
+    const unsigned char pw[] = {};
+    unsigned char *msg;
+    size_t len;
+
+    mbedtls_ecjpake_init( &ctx );
+
+    msg = unhexify_alloc( data, &len );
+    TEST_ASSERT( msg != NULL );
+
+    TEST_ASSERT( mbedtls_ecjpake_setup( &ctx, role,
+                 MBEDTLS_MD_SHA256, MBEDTLS_ECP_DP_SECP256R1, pw, 0 ) == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_read_round_one( &ctx, msg, len ) == ref_ret );
+
+exit:
+    mbedtls_ecjpake_free( &ctx );
+    mbedtls_free( msg );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C */
+void read_round_two_cli( char *data, int ref_ret )
+{
+    mbedtls_ecjpake_context ctx;
+    const unsigned char pw[] = {};
+    unsigned char *msg;
+    size_t len;
+
+    mbedtls_ecjpake_init( &ctx );
+
+    msg = unhexify_alloc( data, &len );
+    TEST_ASSERT( msg != NULL );
+
+    TEST_ASSERT( mbedtls_ecjpake_setup( &ctx, MBEDTLS_ECJPAKE_CLIENT,
+                 MBEDTLS_MD_SHA256, MBEDTLS_ECP_DP_SECP256R1, pw, 0 ) == 0 );
+
+    TEST_ASSERT( ecjpake_test_load( &ctx,
+                 ADD_SIZE( ecjpake_test_x1 ), ADD_SIZE( ecjpake_test_x2 ),
+                 ADD_SIZE( ecjpake_test_X1 ), ADD_SIZE( ecjpake_test_X2 ),
+                 ADD_SIZE( ecjpake_test_X3 ), ADD_SIZE( ecjpake_test_X4 ) )
+            == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_read_round_two( &ctx, msg, len ) == ref_ret );
+
+exit:
+    mbedtls_ecjpake_free( &ctx );
+    mbedtls_free( msg );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C */
+void read_round_two_srv( char *data, int ref_ret )
+{
+    mbedtls_ecjpake_context ctx;
+    const unsigned char pw[] = {};
+    unsigned char *msg;
+    size_t len;
+
+    mbedtls_ecjpake_init( &ctx );
+
+    msg = unhexify_alloc( data, &len );
+    TEST_ASSERT( msg != NULL );
+
+    TEST_ASSERT( mbedtls_ecjpake_setup( &ctx, MBEDTLS_ECJPAKE_SERVER,
+                 MBEDTLS_MD_SHA256, MBEDTLS_ECP_DP_SECP256R1, pw, 0 ) == 0 );
+
+    TEST_ASSERT( ecjpake_test_load( &ctx,
+                 ADD_SIZE( ecjpake_test_x3 ), ADD_SIZE( ecjpake_test_x4 ),
+                 ADD_SIZE( ecjpake_test_X3 ), ADD_SIZE( ecjpake_test_X4 ),
+                 ADD_SIZE( ecjpake_test_X1 ), ADD_SIZE( ecjpake_test_X2 ) )
+            == 0 );
+
+    TEST_ASSERT( mbedtls_ecjpake_read_round_two( &ctx, msg, len ) == ref_ret );
+
+exit:
+    mbedtls_ecjpake_free( &ctx );
+    mbedtls_free( msg );
+}
+/* END_CASE */