diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h
index 35b8eedc0..9a0c19254 100644
--- a/include/mbedtls/ssl_internal.h
+++ b/include/mbedtls/ssl_internal.h
@@ -740,6 +740,50 @@ int mbedtls_ssl_get_key_exchange_md_tls1_2( mbedtls_ssl_context *ssl,
 #endif /* MBEDTLS_SSL_PROTO_TLS1 || MBEDTLS_SSL_PROTO_TLS1_1 || \
           MBEDTLS_SSL_PROTO_TLS1_2 */
 
+#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) && \
+    ( defined(MBEDTLS_SSL_PROTO_TLS1) ||        \
+      defined(MBEDTLS_SSL_PROTO_TLS1_1) |       \
+      defined(MBEDTLS_SSL_PROTO_TLS1_2) )
+/** \brief Compute the HMAC of variable-length data with constant flow.
+ *
+ * This function computes the HMAC of the concatenation of \p add_data and \p
+ * data, and does with a code flow and memory access pattern that does not
+ * depend on \p data_len_secret, but only on \p min_data_len and \p
+ * max_data_len. In particular, this function always reads exactly \p
+ * max_data_len bytes from \p data.
+ *
+ * \param ctx               The HMAC context. It must have keys configured
+ *                          with mbedtls_md_hmac_starts(). It is reset using
+ *                          mbedtls_md_hmac_reset() after the computation is
+ *                          complete to prepare for the next computation.
+ * \param add_data          The additional data prepended to \p data. This
+ *                          must point to a readable buffer of \p add_data_len
+ *                          bytes.
+ * \param add_data_len      The length of \p add_data in bytes.
+ * \param data              The data appended to \p add_data. This must point
+ *                          to a readable buffer of \p max_data_len bytes.
+ * \param data_len_secret   The length of the data to process in \p data.
+ *                          This must be no less than \p min_data_len and no
+ *                          greated than \p max_data_len.
+ * \param min_data_len      The minimal length of \p data in bytes.
+ * \param max_data_len      The maximal length of \p data in bytes.
+ * \param output            The HMAC will be written here. This must point to
+ *                          a writeable buffer of sufficient size to hold the
+ *                          HMAC value.
+ *
+ * \retval 0
+ *         Success.
+ * \retval non-zero
+ *         Failure.
+ */
+int mbedtls_ssl_cf_hmac(
+        mbedtls_md_context_t *ctx,
+        const unsigned char *add_data, size_t add_data_len,
+        const unsigned char *data, size_t data_len_secret,
+        size_t min_data_len, size_t max_data_len,
+        unsigned char *output );
+#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC && TLS 1.0-1.2 */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 7d9eae4ae..c2e5bde99 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -1659,6 +1659,32 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
     return( 0 );
 }
 
+#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) && \
+    ( defined(MBEDTLS_SSL_PROTO_TLS1) ||        \
+      defined(MBEDTLS_SSL_PROTO_TLS1_1) ||      \
+      defined(MBEDTLS_SSL_PROTO_TLS1_2) )
+/*
+ * Compute HMAC of variable-length data with constant flow.
+ */
+int mbedtls_ssl_cf_hmac(
+        mbedtls_md_context_t *ctx,
+        const unsigned char *add_data, size_t add_data_len,
+        const unsigned char *data, size_t data_len_secret,
+        size_t min_data_len, size_t max_data_len,
+        unsigned char *output )
+{
+    /* WORK IN PROGRESS - THIS IS NOT CONSTANT FLOW AT ALL */
+    (void) min_data_len;
+    (void) max_data_len;
+    mbedtls_md_hmac_update( ctx, add_data, add_data_len );
+    mbedtls_md_hmac_update( ctx, data, data_len_secret );
+    mbedtls_md_hmac_finish( ctx, output );
+    mbedtls_md_hmac_reset( ctx );
+
+    return( 0 );
+}
+#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC && TLS 1.0-1.2 */
+
 static int ssl_decrypt_buf( mbedtls_ssl_context *ssl )
 {
     size_t i;
diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data
index b92c1fe8a..f85d26b11 100644
--- a/tests/suites/test_suite_ssl.data
+++ b/tests/suites/test_suite_ssl.data
@@ -57,3 +57,19 @@ ssl_dtls_replay:"abcd12340000,abcd12340100":"abcd123400ff":0
 
 SSL SET_HOSTNAME memory leak: call ssl_set_hostname twice
 ssl_set_hostname_twice:"server0":"server1"
+
+Constant-flow HMAC: MD5
+depends_on:MBEDTLS_MD5_C
+ssl_cf_hmac:MBEDTLS_MD_MD5
+
+Constant-flow HMAC: SHA1
+depends_on:MBEDTLS_SHA1_C
+ssl_cf_hmac:MBEDTLS_MD_SHA1
+
+Constant-flow HMAC: SHA256
+depends_on:MBEDTLS_SHA256_C
+ssl_cf_hmac:MBEDTLS_MD_SHA256
+
+Constant-flow HMAC: SHA384
+depends_on:MBEDTLS_SHA512_C:!MBEDTLS_SHA512_NO_SHA384
+ssl_cf_hmac:MBEDTLS_MD_SHA384
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index 1cd2ed5bb..2eafb2f94 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -54,3 +54,95 @@ void ssl_set_hostname_twice( char *hostname0, char *hostname1 )
 }
 /* END_CASE */
 
+/* BEGIN_CASE depends_on:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_AES_C:MBEDTLS_SSL_PROTO_TLS1_2 */
+void ssl_cf_hmac( int hash )
+{
+    /*
+     * Test the function mbedtls_ssl_cf_hmac() against a reference
+     * implementation.
+     *
+     * Note: the dependency is actually on TLS 1.0-1.2 and (AES or ARIA or
+     * Camellia or DES), but since the test framework doesn't support
+     * alternation in dependencies, just depend on the most common.
+     */
+    mbedtls_md_context_t ctx, ref_ctx;
+    const mbedtls_md_info_t *md_info;
+    size_t out_len, block_size;
+    size_t min_in_len, in_len, max_in_len, i;
+    /* TLS additional data is 13 bytes (hence the "lucky 13" name) */
+    unsigned char add_data[13];
+    unsigned char ref_out[MBEDTLS_MD_MAX_SIZE];
+    unsigned char *data = NULL;
+    unsigned char *out = NULL;
+    unsigned char rec_num = 0;
+
+    mbedtls_md_init( &ctx );
+    mbedtls_md_init( &ref_ctx );
+
+    md_info = mbedtls_md_info_from_type( hash );
+    TEST_ASSERT( md_info != NULL );
+    out_len = mbedtls_md_get_size( md_info );
+    TEST_ASSERT( out_len != 0 );
+    block_size = hash == MBEDTLS_MD_SHA384 ? 128 : 64;
+
+    /* Use allocated out buffer to catch overwrites */
+    out = mbedtls_calloc( 1, out_len );
+    TEST_ASSERT( out != NULL );
+
+    /* Set up contexts with the given hash and a dummy key */
+    TEST_ASSERT( 0 == mbedtls_md_setup( &ctx, md_info, 1 ) );
+    TEST_ASSERT( 0 == mbedtls_md_setup( &ref_ctx, md_info, 1 ) );
+    memset( ref_out, 42, sizeof( ref_out ) );
+    TEST_ASSERT( 0 == mbedtls_md_hmac_starts( &ctx, ref_out, out_len ) );
+    TEST_ASSERT( 0 == mbedtls_md_hmac_starts( &ref_ctx, ref_out, out_len ) );
+    memset( ref_out, 0, sizeof( ref_out ) );
+
+    /*
+     * Test all possible lengths up to a point. The difference between
+     * max_in_len and min_in_len is at most 255, and make sure they both vary
+     * by at least one block size.
+     */
+    for( max_in_len = 0; max_in_len <= 255 + block_size; max_in_len++ )
+    {
+        /* Use allocated in buffer to catch overreads */
+        data = mbedtls_calloc( 1, max_in_len );
+        TEST_ASSERT( data != NULL || max_in_len == 0 );
+
+        min_in_len = max_in_len > 255 ? max_in_len - 255 : 0;
+        for( in_len = min_in_len; in_len <= max_in_len; in_len++ )
+        {
+            /* Set up dummy data and add_data */
+            rec_num++;
+            memset( add_data, rec_num, sizeof( add_data ) );
+            for( i = 0; i < in_len; i++ )
+                data[i] = ( i & 0xff ) ^ rec_num;
+
+            /* Get the function's result */
+            TEST_ASSERT( 0 == mbedtls_ssl_cf_hmac( &ctx, add_data, sizeof( add_data ),
+                                                   data, in_len,
+                                                   min_in_len, max_in_len,
+                                                   out ) );
+
+            /* Compute the reference result */
+            TEST_ASSERT( 0 == mbedtls_md_hmac_update( &ref_ctx, add_data,
+                                                      sizeof( add_data ) ) );
+            TEST_ASSERT( 0 == mbedtls_md_hmac_update( &ref_ctx, data, in_len ) );
+            TEST_ASSERT( 0 == mbedtls_md_hmac_finish( &ref_ctx, ref_out ) );
+            TEST_ASSERT( 0 == mbedtls_md_hmac_reset( &ref_ctx ) );
+
+            /* Compare */
+            TEST_ASSERT( 0 == memcmp( out, ref_out, out_len ) );
+        }
+
+        mbedtls_free( data );
+        data = NULL;
+    }
+
+exit:
+    mbedtls_md_free( &ref_ctx );
+    mbedtls_md_free( &ctx );
+
+    mbedtls_free( data );
+    mbedtls_free( out );
+}
+/* END_CASE */