Merge remote-tracking branch 'upstream-public/pr/1629' into evaluation
diff --git a/include/mbedtls/ccm.h b/include/mbedtls/ccm.h
index 8585ce5..1bc79d8 100644
--- a/include/mbedtls/ccm.h
+++ b/include/mbedtls/ccm.h
@@ -66,6 +66,55 @@
 #endif /* MBEDTLS_CCM_ALT */
 
 /**
+ * \brief           The CCM* callback for encrypting with variable tag length.
+ *                  The function pointer is passed to the APIs called. This
+ *                  function calculates the nonce and returns it in a buffer.
+ *
+ * \warning         This function must not return the same nonce more than once
+ *                  in the lifetime of the key!
+ *
+ * \note            To prevent attacks taking advantage of the variable tag
+ *                  length CCM* encodes the tag length in the nonce. The method
+ *                  of encoding may vary. Standards might mandate encoding other
+ *                  information in the nonce (e.g. address and frame counter)
+ *                  too.
+ *
+ * \param app_ctx   A pointer to structure containing the application context
+ *                  if it is necessary for calculating the initialisation vector
+ *                  (nonce).
+ * \param tag_len   Length of the tag in bytes.
+ * \nonce           Output variable, points to the buffer capable of holding the
+ *                  calculated nonce. Must be at least \p nonce_len bytes long.
+ * \nonce_len       The length of the nonce in bytes.
+ *
+ * \return          \c 0 on success.
+ * \return          MBEDTLS_ERR_CCM_BAD_INPUT error code on failure.
+ */
+typedef int (*mbedtls_ccm_star_get_nonce_t)( void *app_ctx, size_t tag_len,
+                                             unsigned char *nonce,
+                                             size_t nonce_len );
+
+/**
+ * \brief           The CCM* callback for decrypting with variable tag length.
+ *                  The function pointer is passed to the APIs called. This
+ *                  function calculates and returns the length of the tag in the
+ *                  output parameter.
+ *
+ * \param app_ctx   A pointer to structure containing the application context
+ *                  if it is necessary for decoding the tag length or validating
+ *                  the initialisation vector (nonce).
+ * \param tag_len   Output variable for holding the tag length in bytes.
+ * \nonce           A buffer containing the nonce.
+ * \nonce_len       The length of the nonce in bytes.
+ *
+ * \return          \c 0 on success.
+ * \return          MBEDTLS_ERR_CCM_BAD_INPUT error code on failure.
+ */
+typedef int (*mbedtls_ccm_star_get_tag_len_t)( void *app_ctx, size_t* tag_len,
+                                               const unsigned char *nonce,
+                                               size_t nonce_len );
+
+/**
  * \brief           This function initializes the specified CCM context,
  *                  to make references valid, and prepare the context
  *                  for mbedtls_ccm_setkey() or mbedtls_ccm_free().
@@ -102,7 +151,6 @@
 /**
  * \brief           This function encrypts a buffer using CCM.
  *
- *
  * \note            The tag is written to a separate buffer. To concatenate
  *                  the \p tag with the \p output, as done in <em>RFC-3610:
  *                  Counter with CBC-MAC (CCM)</em>, use
@@ -133,6 +181,82 @@
                          unsigned char *tag, size_t tag_len );
 
 /**
+ * \brief           This function encrypts a buffer using CCM* with fixed tag
+ *                  length.
+ *
+ * \note            The tag is written to a separate buffer. To concatenate
+ *                  the \p tag with the \p output, as done in <em>RFC-3610:
+ *                  Counter with CBC-MAC (CCM)</em>, use
+ *                  \p tag = \p output + \p length, and make sure that the
+ *                  output buffer is at least \p length + \p tag_len wide.
+ *
+ * \param ctx       The CCM context to use for encryption.
+ * \param length    The length of the input data in Bytes.
+ * \param iv        Initialization vector (nonce).
+ * \param iv_len    The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, or 13.
+ * \param add       The additional data field.
+ * \param add_len   The length of additional data in Bytes.
+ *                  Must be less than 2^16 - 2^8.
+ * \param input     The buffer holding the input data.
+ * \param output    The buffer holding the output data.
+ *                  Must be at least \p length Bytes wide.
+ * \param tag       The buffer holding the tag.
+ * \param tag_len   The length of the tag to generate in Bytes:
+ *                  0, 4, 6, 8, 10, 12, 14 or 16.
+ *
+ * \warning         Passing 0 as \p tag_len means that the message is no
+ *                  longer authenticated.
+ *
+ * \return          \c 0 on success.
+ * \return          A CCM or cipher-specific error code on failure.
+ */
+int mbedtls_ccm_sfix_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
+                         const unsigned char *iv, size_t iv_len,
+                         const unsigned char *add, size_t add_len,
+                         const unsigned char *input, unsigned char *output,
+                         unsigned char *tag, size_t tag_len );
+
+/**
+ * \brief               This function encrypts a buffer using CCM* with variable
+ *                      tag length.
+ *
+ * \note                The tag is written to a separate buffer. To concatenate
+ *                      the \p tag with the \p output, as done in <em>RFC-3610:
+ *                      Counter with CBC-MAC (CCM)</em>, use
+ *                      \p tag = \p output + \p length, and make sure that the
+ *                      output buffer is at least \p length + \p tag_len wide.
+ *
+ * \param ctx           The CCM context to use for encryption.
+ * \param length        The length of the input data in Bytes.
+ * \param iv_len        The length of the IV in Bytes: 7, 8, 9, 10, 11, 12,
+ *                      or 13.
+ * \param add           The additional data field.
+ * \param add_len       The length of additional data in Bytes.
+ *                      Must be less than 2^16 - 2^8.
+ * \param input         The buffer holding the input data.
+ * \param output        The buffer holding the output data.
+ *                      Must be at least \p length Bytes wide.
+ * \param tag           The buffer holding the tag.
+ * \param tag_len       The length of the tag to generate in Bytes:
+ *                      0, 4, 6, 8, 10, 12, 14 or 16.
+ * \param get_iv        A callback function returning the IV (nonce) with the
+ *                      tag length encoded in it.
+ * \param get_iv_ctx    Context passed to the \p get_iv callback.
+ *
+ * \warning             Passing 0 as \p tag_len means that the message is no
+ *                      longer authenticated.
+ *
+ * \return              \c 0 on success.
+ * \return              A CCM or cipher-specific error code on failure.
+ */
+int mbedtls_ccm_svar_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
+                         size_t iv_len, const unsigned char *add,
+                         size_t add_len, const unsigned char *input,
+                         unsigned char *output, unsigned char *tag,
+                         size_t tag_len, mbedtls_ccm_star_get_nonce_t get_iv,
+                         void *get_iv_ctx );
+
+/**
  * \brief           This function performs a CCM authenticated decryption of a
  *                  buffer.
  *
@@ -160,6 +284,74 @@
                       const unsigned char *input, unsigned char *output,
                       const unsigned char *tag, size_t tag_len );
 
+/**
+ * \brief           This function performs a CCM* authenticated decryption of a
+ *                  buffer with fixed tag length.
+ *
+ * \param ctx       The CCM context to use for decryption.
+ * \param length    The length of the input data in Bytes.
+ * \param iv        Initialization vector.
+ * \param iv_len    The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, or 13.
+ * \param add       The additional data field.
+ * \param add_len   The length of additional data in Bytes.
+ *                  Must be less than 2^16 - 2^8.
+ * \param input     The buffer holding the input data.
+ * \param output    The buffer holding the output data.
+ *                  Must be at least \p length Bytes wide.
+ * \param tag       The buffer holding the tag.
+ * \param tag_len   The length of the tag in Bytes.
+ *                  0, 4, 6, 8, 10, 12, 14 or 16.
+ *
+ * \warning         Passing 0 as \p tag_len means that the message is no
+ *                  longer authenticated.
+ *
+ * \return          \c 0 on success. This indicates that the message is
+ *                  authentic.
+ * \return          #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match.
+ * \return          A cipher-specific error code on calculation failure.
+ */
+int mbedtls_ccm_sfix_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
+                      const unsigned char *iv, size_t iv_len,
+                      const unsigned char *add, size_t add_len,
+                      const unsigned char *input, unsigned char *output,
+                      const unsigned char *tag, size_t tag_len );
+
+/**
+ * \brief               This function performs a CCM* authenticated decryption
+ *                      of a buffer with variable tag length.
+ *
+ * \param ctx           The CCM context to use for decryption.
+ * \param length        The length of the input data in Bytes.
+ * \param iv            Initialization vector.
+ * \param iv_len        The length of the IV in Bytes: 7, 8, 9, 10, 11, 12,
+ *                      or 13.
+ * \param add           The additional data field.
+ * \param add_len       The length of additional data in Bytes.
+ *                      Must be less than 2^16 - 2^8.
+ * \param input         The buffer holding the input data. Unlike the \p input
+ *                      parameters of other Mbed TLS CCM functions, this buffer
+ *                      holds the concatenation of the encrypted data and the
+ *                      authentication tag.
+ * \param output        The buffer holding the output data.
+ *                      Must be at least \p length Bytes wide.
+ * \param output_len    The length of the decrypted data.
+ * \param get_tag_len   A callback function returning the tag length.
+ * \param get_tlen_ctx  Context passed to the \p get_tag_len callback.
+ *
+ *
+ * \return              \c 0 on success. This indicates that the message is
+ *                      authentic.
+ * \return              #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match.
+ * \return              A cipher-specific error code on calculation failure.
+ */
+int mbedtls_ccm_svar_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
+                      const unsigned char *iv, size_t iv_len,
+                      const unsigned char *add, size_t add_len,
+                      const unsigned char *input, unsigned char *output,
+                      size_t* output_len,
+                      mbedtls_ccm_star_get_tag_len_t get_tag_len,
+                      void *get_tlen_ctx );
+
 
 #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C)
 /**
diff --git a/library/ccm.c b/library/ccm.c
index cf65209..72b82a0 100644
--- a/library/ccm.c
+++ b/library/ccm.c
@@ -154,7 +154,13 @@
      * 'length' checked later (when writing it to the first block)
      */
     if( tag_len < 4 || tag_len > 16 || tag_len % 2 != 0 )
-        return( MBEDTLS_ERR_CCM_BAD_INPUT );
+    {
+        /*
+         * Loosen the requirements to enable support for CCM* (IEEE 802.15.4)
+         */
+        if( tag_len != 0 )
+            return( MBEDTLS_ERR_CCM_BAD_INPUT );
+    }
 
     /* Also implies q is within bounds */
     if( iv_len < 7 || iv_len > 13 )
@@ -302,7 +308,7 @@
 /*
  * Authenticated encryption
  */
-int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
+int mbedtls_ccm_sfix_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
                          const unsigned char *iv, size_t iv_len,
                          const unsigned char *add, size_t add_len,
                          const unsigned char *input, unsigned char *output,
@@ -312,10 +318,41 @@
                             add, add_len, input, output, tag, tag_len ) );
 }
 
+int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
+                         const unsigned char *iv, size_t iv_len,
+                         const unsigned char *add, size_t add_len,
+                         const unsigned char *input, unsigned char *output,
+                         unsigned char *tag, size_t tag_len )
+{
+    if( tag_len == 0 )
+        return( MBEDTLS_ERR_CCM_BAD_INPUT );
+
+    return( mbedtls_ccm_sfix_encrypt_and_tag( ctx, length, iv, iv_len, add,
+                add_len, input, output, tag, tag_len ) );
+}
+
+#define CCM_MAX_IV_LEN 13
+
+int mbedtls_ccm_svar_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
+                         size_t iv_len, const unsigned char *add,
+                         size_t add_len, const unsigned char *input,
+                         unsigned char *output, unsigned char *tag,
+                         size_t tag_len, mbedtls_ccm_star_get_nonce_t get_iv,
+                         void *get_iv_ctx )
+{
+    unsigned char iv[CCM_MAX_IV_LEN];
+
+    if( get_iv( get_iv_ctx, tag_len, iv, iv_len ) != 0 )
+        return MBEDTLS_ERR_CCM_BAD_INPUT;
+
+    return( mbedtls_ccm_sfix_encrypt_and_tag( ctx, length, iv, iv_len, add,
+                add_len, input, output, tag, tag_len ) );
+}
+
 /*
  * Authenticated decryption
  */
-int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
+int mbedtls_ccm_sfix_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
                       const unsigned char *iv, size_t iv_len,
                       const unsigned char *add, size_t add_len,
                       const unsigned char *input, unsigned char *output,
@@ -346,6 +383,38 @@
     return( 0 );
 }
 
+int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
+                      const unsigned char *iv, size_t iv_len,
+                      const unsigned char *add, size_t add_len,
+                      const unsigned char *input, unsigned char *output,
+                      const unsigned char *tag, size_t tag_len )
+{
+    if( tag_len == 0 )
+        return( MBEDTLS_ERR_CCM_BAD_INPUT );
+
+    return( mbedtls_ccm_sfix_auth_decrypt( ctx, length, iv, iv_len, add,
+                add_len, input, output, tag, tag_len ) );
+}
+
+
+int mbedtls_ccm_svar_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
+                      const unsigned char *iv, size_t iv_len,
+                      const unsigned char *add, size_t add_len,
+                      const unsigned char *input, unsigned char *output,
+                      size_t* output_len,
+                      mbedtls_ccm_star_get_tag_len_t get_tag_len,
+                      void *get_tlen_ctx )
+{
+    size_t tag_len = 0;
+
+    if( get_tag_len( get_tlen_ctx, &tag_len, iv, iv_len ) != 0 )
+        return( MBEDTLS_ERR_CCM_BAD_INPUT );
+
+    *output_len = length - tag_len;
+
+    return( mbedtls_ccm_sfix_auth_decrypt( ctx, length, iv, iv_len, add,
+                add_len, input, output, input + length, tag_len ) );
+}
 #endif /* !MBEDTLS_CCM_ALT */
 
 #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C)
diff --git a/tests/suites/test_suite_ccm.data b/tests/suites/test_suite_ccm.data
index 90ba42d..a55ece1 100644
--- a/tests/suites/test_suite_ccm.data
+++ b/tests/suites/test_suite_ccm.data
@@ -41,6 +41,15 @@
 CCM lengths #8 msg too long for this IV length (2^16, q = 2)
 ccm_lengths:65536:13:5:8:MBEDTLS_ERR_CCM_BAD_INPUT
 
+CCM lengths #9 tag length 0
+ccm_lengths:5:10:5:0:MBEDTLS_ERR_CCM_BAD_INPUT
+
+CCM* fixed tag lengths #1 all OK
+ccm_sfix_lengths:5:10:5:8:0
+
+CCM* fixed tag lengths #2 all OK - tag length 0
+ccm_sfix_lengths:5:10:5:0:0
+
 CCM encrypt and tag RFC 3610 #1
 depends_on:MBEDTLS_AES_C
 mbedtls_ccm_encrypt_and_tag:MBEDTLS_CIPHER_ID_AES:"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF":"08090A0B0C0D0E0F101112131415161718191A1B1C1D1E":"00000003020100A0A1A2A3A4A5":"0001020304050607":"588C979A61C663D2F066D0C2C0F989806D5F6B61DAC38417E8D12CFDF926E0"
diff --git a/tests/suites/test_suite_ccm.function b/tests/suites/test_suite_ccm.function
index 2f5c77c..d64b697 100644
--- a/tests/suites/test_suite_ccm.function
+++ b/tests/suites/test_suite_ccm.function
@@ -74,6 +74,47 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE depends_on:MBEDTLS_AES_C */
+void ccm_sfix_lengths( int msg_len, int iv_len, int add_len, int tag_len,
+                       int res )
+{
+    mbedtls_ccm_context ctx;
+    unsigned char key[16];
+    unsigned char msg[10];
+    unsigned char iv[14];
+    unsigned char add[10];
+    unsigned char out[10];
+    unsigned char tag[18];
+    int decrypt_ret;
+
+    mbedtls_ccm_init( &ctx );
+
+    memset( key, 0, sizeof( key ) );
+    memset( msg, 0, sizeof( msg ) );
+    memset( iv, 0, sizeof( iv ) );
+    memset( add, 0, sizeof( add ) );
+    memset( out, 0, sizeof( out ) );
+    memset( tag, 0, sizeof( tag ) );
+
+    TEST_ASSERT( mbedtls_ccm_setkey( &ctx, MBEDTLS_CIPHER_ID_AES,
+                                 key, 8 * sizeof( key ) ) == 0 );
+
+    TEST_ASSERT( mbedtls_ccm_sfix_encrypt_and_tag( &ctx, msg_len, iv, iv_len,
+                 add, add_len, msg, out, tag, tag_len ) == res );
+
+    decrypt_ret = mbedtls_ccm_sfix_auth_decrypt( &ctx, msg_len, iv, iv_len, add,
+                  add_len, msg, out, tag, tag_len );
+
+    if( res == 0 && tag_len != 0 )
+        TEST_ASSERT( decrypt_ret == MBEDTLS_ERR_CCM_AUTH_FAILED );
+    else
+        TEST_ASSERT( decrypt_ret == res );
+
+exit:
+    mbedtls_ccm_free( &ctx );
+}
+/* END_CASE */
+
 /* BEGIN_CASE */
 void mbedtls_ccm_encrypt_and_tag( int cipher_id,
                           char *key_hex, char *msg_hex,