Merge changes for leaner memory footprint
diff --git a/ChangeLog b/ChangeLog
index 070e55e..dc008f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -22,6 +22,8 @@
      from the default list (inactive by default).
    * Add server-side enforcement of sent renegotiation requests
      (ssl_set_renegotiation_enforced())
+   * Add SSL_CIPHERSUITES config.h flag to allow specifying a list of
+     ciphersuites to use and save some memory if the list is small.
 
 Changes
    * Add LINK_WITH_PTHREAD option in CMake for explicit linking that is
diff --git a/configs/config-ccm-psk-tls1_2.h b/configs/config-ccm-psk-tls1_2.h
index f425391..1da9f6a 100644
--- a/configs/config-ccm-psk-tls1_2.h
+++ b/configs/config-ccm-psk-tls1_2.h
@@ -3,7 +3,7 @@
  * Distinguishing features:
  * - no bignum, no PK, no X509
  * - fully modern and secure (provided the pre-shared keys have high entropy)
- * - very low record overhead if using the CCM-8 suites
+ * - very low record overhead with CCM-8
  * - optimized for low RAM usage
  *
  * See README.txt for usage instructions.
@@ -13,7 +13,7 @@
 
 /* System support */
 //#define POLARSSL_HAVE_IPV6 /* Optional */
-//#define POLARSSL_HAVE_TIME /* Optionnaly used in Hello messages */
+//#define POLARSSL_HAVE_TIME /* Optionally used in Hello messages */
 /* Other POLARSSL_HAVE_XXX flags irrelevant for this configuration */
 
 /* PolarSSL feature support */
@@ -36,6 +36,9 @@
 /* Save RAM at the expense of ROM */
 #define POLARSSL_AES_ROM_TABLES
 
+/* Save some RAM by adjusting to your exact needs */
+#define POLARSSL_PSK_MAX_LEN    16 /* 128-bits keys are generally enough */
+
 /*
  * You should adjust this to the exact number of sources you're using: default
  * is the "platform_entropy_poll" source, but you may want to add other ones
@@ -44,8 +47,16 @@
 #define ENTROPY_MAX_SOURCES 2
 
 /*
+ * Use only CCM_8 ciphersuites, and
+ * save ROM and a few bytes of RAM by specifying our own ciphersuite list
+ */
+#define SSL_CIPHERSUITES                        \
+        TLS_PSK_WITH_AES_256_CCM_8,             \
+        TLS_PSK_WITH_AES_128_CCM_8
+
+/*
  * Save RAM at the expense of interoperability: do this only if you control
- * both ends of the connection!  (See coments in "polarssl/ssl.h".)
+ * both ends of the connection!  (See comments in "polarssl/ssl.h".)
  * The optimal size here depends on the typical size of records.
  */
 #define SSL_MAX_CONTENT_LEN             512
diff --git a/configs/config-suite-b.h b/configs/config-suite-b.h
index d10cf63..cd38f33 100644
--- a/configs/config-suite-b.h
+++ b/configs/config-suite-b.h
@@ -77,6 +77,11 @@
  */
 #define ENTROPY_MAX_SOURCES 2
 
+/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */
+#define SSL_CIPHERSUITES                        \
+    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,    \
+    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+
 /*
  * Save RAM at the expense of interoperability: do this only if you control
  * both ends of the connection!  (See coments in "polarssl/ssl.h".)
diff --git a/include/polarssl/config.h b/include/polarssl/config.h
index bfd68c4..d43365f 100644
--- a/include/polarssl/config.h
+++ b/include/polarssl/config.h
@@ -2154,6 +2154,21 @@
 /* SSL options */
 //#define SSL_MAX_CONTENT_LEN             16384 /**< Size of the input / output buffer */
 //#define SSL_DEFAULT_TICKET_LIFETIME     86400 /**< Lifetime of session tickets (if enabled) */
+//#define POLARSSL_PSK_MAX_LEN               32 /**< Max size of TLS pre-shared keys, in bytes (default 256 bits) */
+
+/**
+ * Complete list of ciphersuites to use, in order of preference.
+ *
+ * \warning No dependency checking is done on that field! This option can only
+ * be used to restrict the set of available ciphersuites. It is your
+ * responsibility to make sure the needed modules are active.
+ *
+ * Use this to save a few hundred bytes of ROM (default ordering of all
+ * available ciphersuites) and a few to a few hundred bytes of RAM.
+ *
+ * The value below is only an example, not the default.
+ */
+//#define SSL_CIPHERSUITES TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
 
 /* Debug options */
 //#define POLARSSL_DEBUG_DFL_MODE POLARSSL_DEBUG_LOG_FULL /**< Default log: Full or Raw */
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index b90b232..730dc39 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -34,6 +34,7 @@
 #endif
 #include "net.h"
 #include "bignum.h"
+#include "ecp.h"
 
 #include "ssl_ciphersuites.h"
 
@@ -252,7 +253,9 @@
  * Note: the RFC defines the default size of SSL / TLS messages. If you
  * change the value here, other clients / servers may not be able to
  * communicate with you anymore. Only change this value if you control
- * both sides of the connection and have it reduced at both sides!
+ * both sides of the connection and have it reduced at both sides, or
+ * if you're using the Max Fragment Length extension and you know all your
+ * peers are using it too!
  */
 #if !defined(SSL_MAX_CONTENT_LEN)
 #define SSL_MAX_CONTENT_LEN         16384   /**< Size of the input / output buffer */
@@ -261,8 +264,8 @@
 /* \} name SECTION: Module settings */
 
 /*
- * Allow an extra 301 bytes for the record header and encryption overhead:
- * counter (8) + header (5) + IV(16) + MAC (48) + padding (256)
+ * Allow extra bytes for record, authentication and encryption overhead:
+ * counter (8) + header (5) + IV(16) + MAC (16-48) + padding (0-256)
  * and allow for a maximum of 1024 of compression expansion if
  * enabled.
  */
@@ -272,8 +275,36 @@
 #define SSL_COMPRESSION_ADD             0
 #endif
 
-#define SSL_BUFFER_LEN (SSL_MAX_CONTENT_LEN + SSL_COMPRESSION_ADD + 333)
+#if defined(POLARSSL_RC4_C) || defined(POLARSSL_CIPHER_MODE_CBC)
+/* Ciphersuites using HMAC */
+#if defined(POLARSSL_SHA512_C)
+#define SSL_MAC_ADD                 48  /* SHA-384 used for HMAC */
+#elif defined(POLARSSL_SHA256_C)
+#define SSL_MAC_ADD                 32  /* SHA-256 used for HMAC */
+#else
+#define SSL_MAC_ADD                 20  /* SHA-1   used for HMAC */
+#endif
+#else
+/* AEAD ciphersuites: GCM and CCM use a 128 bits tag */
+#define SSL_MAC_ADD                 16
+#endif
 
+#if defined(POLARSSL_CIPHER_MODE_CBC)
+#define SSL_PADDING_ADD            256
+#else
+#define SSL_PADDING_ADD              0
+#endif
+
+#define SSL_BUFFER_LEN  ( SSL_MAX_CONTENT_LEN               \
+                        + SSL_COMPRESSION_ADD               \
+                        + 29 /* counter + header + IV */    \
+                        + SSL_MAC_ADD                       \
+                        + SSL_PADDING_ADD                   \
+                        )
+
+/*
+ * Signaling ciphersuite values (SCSV)
+ */
 #define SSL_EMPTY_RENEGOTIATION_INFO    0xFF   /**< renegotiation info ext */
 
 /*
@@ -382,12 +413,43 @@
 /*
  * Size defines
  */
-#if !defined(POLARSSL_MPI_MAX_SIZE)
-#define POLARSSL_PREMASTER_SIZE             512
-#else
-#define POLARSSL_PREMASTER_SIZE             POLARSSL_MPI_MAX_SIZE
+#if !defined(POLARSSL_PSK_MAX_LEN)
+#define POLARSSL_PSK_MAX_LEN            32 /* 256 bits */
 #endif
 
+/* Dummy type used only for its size */
+union _ssl_premaster_secret
+{
+#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED)
+    unsigned char _pms_rsa[48];                         /* RFC 5246 8.1.1 */
+#endif
+#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED)
+    unsigned char _pms_dhm[POLARSSL_MPI_MAX_SIZE];      /* RFC 5246 8.1.2 */
+#endif
+#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED)    || \
+    defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)  || \
+    defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED)     || \
+    defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED)
+    unsigned char _pms_ecdh[POLARSSL_ECP_MAX_BYTES];    /* RFC 4492 5.10 */
+#endif
+#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED)
+    unsigned char _pms_psk[4 + 2 * POLARSSL_PSK_MAX_LEN];       /* RFC 4279 2 */
+#endif
+#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED)
+    unsigned char _pms_dhe_psk[4 + POLARSSL_MPI_MAX_SIZE
+                                 + POLARSSL_PSK_MAX_LEN];       /* RFC 4279 3 */
+#endif
+#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED)
+    unsigned char _pms_rsa_psk[52 + POLARSSL_PSK_MAX_LEN];      /* RFC 4279 4 */
+#endif
+#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED)
+    unsigned char _pms_ecdhe_psk[4 + POLARSSL_ECP_MAX_BYTES
+                                   + POLARSSL_PSK_MAX_LEN];     /* RFC 5489 2 */
+#endif
+};
+
+#define POLARSSL_PREMASTER_SIZE     sizeof( union _ssl_premaster_secret )
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/include/polarssl/ssl_ciphersuites.h b/include/polarssl/ssl_ciphersuites.h
index 5ecd5fe..c4f1ffe 100644
--- a/include/polarssl/ssl_ciphersuites.h
+++ b/include/polarssl/ssl_ciphersuites.h
@@ -233,6 +233,7 @@
 #define TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8      0xC0AE  /**< TLS 1.2 */
 #define TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8      0xC0AF  /**< TLS 1.2 */
 
+/* Reminder: update _ssl_premaster_secret when adding a new key exchange */
 typedef enum {
     POLARSSL_KEY_EXCHANGE_NONE = 0,
     POLARSSL_KEY_EXCHANGE_RSA,
diff --git a/library/ssl_ciphersuites.c b/library/ssl_ciphersuites.c
index 608e26d..df838e2 100644
--- a/library/ssl_ciphersuites.c
+++ b/library/ssl_ciphersuites.c
@@ -57,6 +57,9 @@
  */
 static const int ciphersuite_preference[] =
 {
+#if defined(SSL_CIPHERSUITES)
+    SSL_CIPHERSUITES,
+#else
     /* All AES-256 ephemeral suites */
     TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
     TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
@@ -257,13 +260,10 @@
     TLS_PSK_WITH_NULL_SHA256,
     TLS_PSK_WITH_NULL_SHA,
 
+#endif
     0
 };
 
-#define MAX_CIPHERSUITES    176
-static int supported_ciphersuites[MAX_CIPHERSUITES];
-static int supported_init = 0;
-
 static const ssl_ciphersuite_t ciphersuite_definitions[] =
 {
 #if defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
@@ -1679,6 +1679,17 @@
     { 0, "", 0, 0, 0, 0, 0, 0, 0, 0 }
 };
 
+#if defined(SSL_CIPHERSUITES)
+const int *ssl_list_ciphersuites( void )
+{
+    return( ciphersuite_preference );
+}
+#else
+#define MAX_CIPHERSUITES    sizeof( ciphersuite_definitions     ) /         \
+                            sizeof( ciphersuite_definitions[0]  )
+static int supported_ciphersuites[MAX_CIPHERSUITES];
+static int supported_init = 0;
+
 const int *ssl_list_ciphersuites( void )
 {
     /*
@@ -1687,21 +1698,21 @@
      */
     if( supported_init == 0 )
     {
-        const int *p = ciphersuite_preference;
-        int *q = supported_ciphersuites;
-        size_t i;
-        size_t max = sizeof(supported_ciphersuites) / sizeof(int);
+        const int *p;
+        int *q;
 
-        for( i = 0; i < max - 1 && p[i] != 0; i++ )
+        for( p = ciphersuite_preference, q = supported_ciphersuites;
+             *p != 0 && q < supported_ciphersuites + MAX_CIPHERSUITES - 1;
+             p++ )
         {
 #if defined(POLARSSL_REMOVE_ARC4_CIPHERSUITES)
             const ssl_ciphersuite_t *cs_info;
-            if( ( cs_info = ssl_ciphersuite_from_id( p[i] ) ) != NULL &&
+            if( ( cs_info = ssl_ciphersuite_from_id( *p ) ) != NULL &&
                 cs_info->cipher != POLARSSL_CIPHER_ARC4_128 )
 #else
-            if( ssl_ciphersuite_from_id( p[i] ) != NULL )
+            if( ssl_ciphersuite_from_id( *p ) != NULL )
 #endif
-                *(q++) = p[i];
+                *(q++) = *p;
         }
         *q = 0;
 
@@ -1710,6 +1721,7 @@
 
     return( supported_ciphersuites );
 };
+#endif /* SSL_CIPHERSUITES */
 
 const ssl_ciphersuite_t *ssl_ciphersuite_from_string(
                                                 const char *ciphersuite_name )
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 7c8f306..8040f90 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -3747,12 +3747,7 @@
     if( psk == NULL || psk_identity == NULL )
         return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
 
-    /*
-     * The length will be check later anyway, but in case it is obviously
-     * too large, better abort now. The PMS is as follows:
-     * other_len (2 bytes) + other + psk_len (2 bytes) + psk
-     */
-    if( psk_len + 4 > POLARSSL_PREMASTER_SIZE )
+    if( psk_len > POLARSSL_PSK_MAX_LEN )
         return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
 
     if( ssl->psk != NULL )
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 3af54f9..4682ee5 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -325,7 +325,7 @@
     int ret = 0, len, server_fd, i, written, frags;
     unsigned char buf[SSL_MAX_CONTENT_LEN + 1];
 #if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED)
-    unsigned char psk[256];
+    unsigned char psk[POLARSSL_PSK_MAX_LEN];
     size_t psk_len = 0;
 #endif
 #if defined(POLARSSL_SSL_ALPN)
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 9cb742b..c470363 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -127,8 +127,6 @@
     "<h2>PolarSSL Test Server</h2>\r\n" \
     "<p>Successful connection using: %s</p>\r\n" // LONG_RESPONSE
 
-#define MAX_PSK_LEN     256
-
 /*
  * Size of the basic I/O buffer. Able to hold our default response.
  *
@@ -462,7 +460,7 @@
     size_t j;
 
     *olen = strlen( input );
-    if( *olen % 2 != 0 || *olen / 2 > MAX_PSK_LEN )
+    if( *olen % 2 != 0 || *olen / 2 > POLARSSL_PSK_MAX_LEN )
         return( -1 );
     *olen /= 2;
 
@@ -486,7 +484,7 @@
 {
     const char *name;
     size_t key_len;
-    unsigned char key[MAX_PSK_LEN];
+    unsigned char key[POLARSSL_PSK_MAX_LEN];
     psk_entry *next;
 };
 
@@ -575,7 +573,7 @@
     int version_suites[4][2];
     unsigned char buf[IO_BUF_LEN];
 #if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED)
-    unsigned char psk[MAX_PSK_LEN];
+    unsigned char psk[POLARSSL_PSK_MAX_LEN];
     size_t psk_len = 0;
     psk_entry *psk_info = NULL;
 #endif
diff --git a/tests/scripts/test-ref-configs.pl b/tests/scripts/test-ref-configs.pl
index 9b09e34..6546b05 100755
--- a/tests/scripts/test-ref-configs.pl
+++ b/tests/scripts/test-ref-configs.pl
@@ -20,7 +20,7 @@
     'config-picocoin.h'
         => 0,
     'config-ccm-psk-tls1_2.h'
-        => '-m tls1_2 -f \'TLS-PSK.*AES.*CCM\'',
+        => '-m tls1_2 -f \'^TLS-PSK-WITH-AES-...-CCM-8\'',
 );
 
 # If no config-name is provided, use all known configs.