Merge pull request #6606 from gabor-mezei-arm/6222_bignum_low_level_subtraction

Bignum: Add low level subtraction
diff --git a/ChangeLog.d/driver-only-hashes.txt b/ChangeLog.d/driver-only-hashes.txt
index 2062bcb..6ccd199 100644
--- a/ChangeLog.d/driver-only-hashes.txt
+++ b/ChangeLog.d/driver-only-hashes.txt
@@ -1,20 +1,19 @@
 Features
-   * Some crypto modules that previously depended on MD or a low-level hash
-     module, either unconditionally (RSA, PK, PKCS5, PKCS12, EC J-PAKE), or
-     for some features (PEM for encrypted files), are now able to use PSA
-     Crypto instead when the legacy API is not available. This means it is
-     now possible to use all features from those modules in configurations
-     where the built-in implementations of hashes are excluded and the hashes
-     are only provided by PSA drivers. In these configurations, you need to
-     call `psa_crypto_init()` before you call any function from those
-     modules; this is not required in configurations where the built-in
-     implementation is still available. Note that some crypto modules and
-     features still depend on the built-in implementation of hashes:
-     MBEDTLS_HKDF_C (but the PSA HKDF function do not depend on it),
-     MBEDTLS_ENTROPY_C, MBEDTLS_HMAC_DRBG_C and MBEDTLS_ECDSA_DETERMINISTIC.
-     In particular, for now, compiling without built-in hashes requires use
-     of MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG.
-   * When MBEDTLS_USE_PSA_CRYPTO is enabled, X.509, TLS 1.2 and TLS 1.3 no
-     longer depend on MD. This means it is now possible to use them in
-     configurations where the built-in implementations of hashes are excluded
-     and the hashes are only provided by PSA drivers.
+   * Some modules can now use PSA drivers for hashes, including with no
+     built-in implementation present, but only in some configurations.
+     - RSA OAEP and PSS (PKCS#1 v2.1), PKCS5, PKCS12 and EC J-PAKE now use
+       hashes from PSA when (and only when) MBEDTLS_MD_C is disabled.
+     - PEM parsing of encrypted files now uses MD-5 from PSA when (and only
+       when) MBEDTLS_MD5_C is disabled.
+     See the documentation of the corresponding macros in mbedtls_config.h for
+     details.
+     Note that some modules are not able to use hashes from PSA yet, including
+     the entropy module. As a consequence, for now the only way to build with
+     all hashes only provided by drivers (no built-in hash) is to use
+     MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG.
+   * When MBEDTLS_USE_PSA_CRYPTO is enabled, X.509, TLS 1.2 and TLS 1.3 now
+     properly negotiate/accept hashes based on their availability in PSA.
+     As a consequence, they now work in configurations where the built-in
+     implementations of (some) hashes are excluded and those hashes are only
+     provided by PSA drivers. (See previous entry for limitation on RSA-PSS
+     though: that module only use hashes from PSA when MBEDTLS_MD_C is off).
diff --git a/ChangeLog.d/fix-possible-false-success-in-mbedtls_cipher_check_tag.txt b/ChangeLog.d/fix-possible-false-success-in-mbedtls_cipher_check_tag.txt
new file mode 100644
index 0000000..1f9e0aa
--- /dev/null
+++ b/ChangeLog.d/fix-possible-false-success-in-mbedtls_cipher_check_tag.txt
@@ -0,0 +1,5 @@
+Changes
+   * Calling AEAD tag-specific functions for non-AEAD algorithms (which
+     should not be done - they are documented for use only by AES-GCM and
+     ChaCha20+Poly1305) now returns MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE
+     instead of success (0).
diff --git a/docs/architecture/tls13-support.md b/docs/architecture/tls13-support.md
index f30590b..85482ba 100644
--- a/docs/architecture/tls13-support.md
+++ b/docs/architecture/tls13-support.md
@@ -478,3 +478,175 @@
 
 * state change: the state change is done in the main state handler to ease
 the navigation of the state machine transitions.
+
+
+Writing and reading early or 0-RTT data
+---------------------------------------
+
+An application function to write and send a buffer of data to a server through
+TLS may plausibly look like:
+
+```
+int write_data( mbedtls_ssl_context *ssl,
+                const unsigned char *data_to_write,
+                size_t data_to_write_len,
+                size_t *data_written )
+{
+    *data_written = 0;
+
+    while( *data_written < data_to_write_len )
+    {
+        ret = mbedtls_ssl_write( ssl, data_to_write + *data_written,
+                                 data_to_write_len - *data_written );
+
+        if( ret < 0 &&
+            ret != MBEDTLS_ERR_SSL_WANT_READ &&
+            ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+        {
+            return( ret );
+        }
+
+        *data_written += ret;
+    }
+
+    return( 0 );
+}
+```
+where ssl is the SSL context to use, data_to_write the address of the data
+buffer and data_to_write_len the number of data bytes. The handshake may
+not be completed, not even started for the SSL context ssl when the function is
+called and in that case the mbedtls_ssl_write() API takes care transparently of
+completing the handshake before to write and send data to the server. The
+mbedtls_ssl_write() may not been able to write and send all data in one go thus
+the need for a loop calling it as long as there are still data to write and
+send.
+
+An application function to write and send early data and only early data,
+data sent during the first flight of client messages while the handshake is in
+its initial phase, would look completely similar but the call to
+mbedtls_ssl_write_early_data() instead of mbedtls_ssl_write().
+```
+int write_early_data( mbedtls_ssl_context *ssl,
+                      const unsigned char *data_to_write,
+                      size_t data_to_write_len,
+                      size_t *data_written )
+{
+    *data_written = 0;
+
+    while( *data_written < data_to_write_len )
+    {
+        ret = mbedtls_ssl_write_early_data( ssl, data_to_write + *data_written,
+                                            data_to_write_len - *data_written );
+
+        if( ret < 0 &&
+            ret != MBEDTLS_ERR_SSL_WANT_READ &&
+            ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+        {
+            return( ret );
+        }
+
+        *data_written += ret;
+    }
+
+    return( 0 );
+}
+```
+Note that compared to write_data(), write_early_data() can also return
+MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA and that should be handled
+specifically by the user of write_early_data(). A fresh SSL context (typically
+just after a call to mbedtls_ssl_setup() or mbedtls_ssl_session_reset()) would
+be expected when calling `write_early_data`.
+
+All together, code to write and send a buffer of data as long as possible as
+early data and then as standard post-handshake application data could
+plausibly look like:
+
+```
+ret = write_early_data( ssl, data_to_write, data_to_write_len,
+                        &early_data_written );
+if( ret < 0 &&
+    ret != MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA )
+{
+    goto error;
+}
+
+ret = write_data( ssl, data_to_write + early_data_written,
+                  data_to_write_len - early_data_written, &data_written );
+if( ret < 0 )
+    goto error;
+
+data_written += early_data_written;
+```
+
+Finally, taking into account that the server may reject early data, application
+code to write and send a buffer of data could plausibly look like:
+```
+ret = write_early_data( ssl, data_to_write, data_to_write_len,
+                        &early_data_written );
+if( ret < 0 &&
+    ret != MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA )
+{
+    goto error;
+}
+
+/*
+ * Make sure the handshake is completed as it is a requisite to
+ * mbedtls_ssl_get_early_data_status().
+ */
+while( !mbedtls_ssl_is_handshake_over( ssl ) )
+{
+    ret = mbedtls_ssl_handshake( ssl );
+    if( ret < 0 &&
+        ret != MBEDTLS_ERR_SSL_WANT_READ &&
+        ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+    {
+        goto error;
+    }
+}
+
+ret = mbedtls_ssl_get_early_data_status( ssl );
+if( ret < 0 )
+    goto error;
+
+if( ret == MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED )
+   early_data_written = 0;
+
+ret = write_data( ssl, data_to_write + early_data_written,
+                  data_to_write_len - early_data_written, &data_written );
+if( ret < 0 )
+    goto error;
+
+data_written += early_data_written;
+```
+
+Basically, the same holds for reading early data on the server side without the
+complication of possible rejection. An application function to read early data
+into a given buffer could plausibly look like:
+```
+int read_early_data( mbedtls_ssl_context *ssl,
+                     unsigned char *buffer,
+                     size_t buffer_size,
+                     size_t *data_len )
+{
+    *data_len = 0;
+
+    while( *data_len < buffer_size )
+    {
+        ret = mbedtls_ssl_read_early_data( ssl, buffer + *data_len,
+                                           buffer_size - *data_len );
+
+        if( ret < 0 &&
+            ret != MBEDTLS_ERR_SSL_WANT_READ &&
+            ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+        {
+            return( ret );
+        }
+
+        *data_len += ret;
+    }
+
+    return( 0 );
+}
+```
+with again calls to read_early_data() expected to be done with a fresh SSL
+context.
diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h
index 12d503e..5433142 100644
--- a/include/mbedtls/mbedtls_config.h
+++ b/include/mbedtls/mbedtls_config.h
@@ -1146,6 +1146,11 @@
  * \warning If building without MBEDTLS_MD_C, you must call psa_crypto_init()
  * before doing any PKCS#1 v2.1 operation.
  *
+ * \warning When building with MBEDTLS_MD_C, all hashes used with this
+ * need to be available as built-ins (that is, for SHA-256, MBEDTLS_SHA256_C,
+ * etc.) as opposed to just PSA drivers. So far, PSA drivers are only used by
+ * this module in builds where MBEDTLS_MD_C is disabled.
+ *
  * This enables support for RSAES-OAEP and RSASSA-PSS operations.
  */
 #define MBEDTLS_PKCS1_V21
@@ -2433,6 +2438,11 @@
  *
  * \warning If building without MBEDTLS_MD_C, you must call psa_crypto_init()
  * before doing any EC J-PAKE operations.
+ *
+ * \warning When building with MBEDTLS_MD_C, all hashes used with this
+ * need to be available as built-ins (that is, for SHA-256, MBEDTLS_SHA256_C,
+ * etc.) as opposed to just PSA drivers. So far, PSA drivers are only used by
+ * this module in builds where MBEDTLS_MD_C is disabled.
  */
 #define MBEDTLS_ECJPAKE_C
 
@@ -2777,6 +2787,11 @@
  * \warning If building without MBEDTLS_MD_C, you must call psa_crypto_init()
  * before doing any PKCS5 operation.
  *
+ * \warning When building with MBEDTLS_MD_C, all hashes used with this
+ * need to be available as built-ins (that is, for SHA-256, MBEDTLS_SHA256_C,
+ * etc.) as opposed to just PSA drivers. So far, PSA drivers are only used by
+ * this module in builds where MBEDTLS_MD_C is disabled.
+ *
  * This module adds support for the PKCS#5 functions.
  */
 #define MBEDTLS_PKCS5_C
@@ -2796,6 +2811,11 @@
  * \warning If building without MBEDTLS_MD_C, you must call psa_crypto_init()
  * before doing any PKCS12 operation.
  *
+ * \warning When building with MBEDTLS_MD_C, all hashes used with this
+ * need to be available as built-ins (that is, for SHA-256, MBEDTLS_SHA256_C,
+ * etc.) as opposed to just PSA drivers. So far, PSA drivers are only used by
+ * this module in builds where MBEDTLS_MD_C is disabled.
+ *
  * This module enables PKCS#12 functions.
  */
 #define MBEDTLS_PKCS12_C
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index dddaaea..ea58661 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -96,15 +96,16 @@
 /* Error space gap */
 /** Processing of the Certificate handshake message failed. */
 #define MBEDTLS_ERR_SSL_BAD_CERTIFICATE                   -0x7A00
+/* Error space gap */
 /**
  * Received NewSessionTicket Post Handshake Message.
  * This error code is experimental and may be changed or removed without notice.
  */
 #define MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET       -0x7B00
-/* Error space gap */
-/* Error space gap */
-/* Error space gap */
-/* Error space gap */
+/** Not possible to read early data */
+#define MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA            -0x7B80
+/** Not possible to write early data */
+#define MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA           -0x7C00
 /* Error space gap */
 /* Error space gap */
 /* Error space gap */
@@ -806,14 +807,6 @@
 typedef struct mbedtls_ssl_flight_item mbedtls_ssl_flight_item;
 #endif
 
-#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C)
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_UNKNOWN           0
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT          1
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_INDICATION_SENT   2
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED          3
-#define MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED          4
-#endif
-
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS)
 typedef uint8_t mbedtls_ssl_tls13_ticket_flags;
 
@@ -4897,6 +4890,151 @@
  */
 int mbedtls_ssl_close_notify( mbedtls_ssl_context *ssl );
 
+#if defined(MBEDTLS_SSL_EARLY_DATA)
+
+#if defined(MBEDTLS_SSL_SRV_C)
+/**
+ * \brief          Read at most 'len' application data bytes while performing
+ *                 the handshake (early data).
+ *
+ * \note           This function behaves mainly as mbedtls_ssl_read(). The
+ *                 specification of mbedtls_ssl_read() relevant to TLS 1.3
+ *                 (thus not the parts specific to (D)TLS 1.2) applies to this
+ *                 function and the present documentation is restricted to the
+ *                 differences with mbedtls_ssl_read().
+ *
+ * \param ssl      SSL context
+ * \param buf      buffer that will hold the data
+ * \param len      maximum number of bytes to read
+ *
+ * \return         One additional specific return value:
+ *                 #MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA.
+ *
+ *                 #MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA is returned when it
+ *                 is not possible to read early data for the SSL context
+ *                 \p ssl.
+ *
+ *                 It may have been possible and it is not possible
+ *                 anymore because the server received the End of Early Data
+ *                 message or the maximum number of allowed early data for the
+ *                 PSK in use has been reached.
+ *
+ *                 It may never have been possible and will never be possible
+ *                 for the SSL context \p ssl because the use of early data
+ *                 is disabled for that context or more generally the context
+ *                 is not suitably configured to enable early data or the
+ *                 client does not use early data or the first call to the
+ *                 function was done while the handshake was already too
+ *                 advanced to gather and accept early data.
+ *
+ *                 It is not possible to read early data for the SSL context
+ *                 \p ssl but this does not preclude for using it with
+ *                 mbedtls_ssl_write(), mbedtls_ssl_read() or
+ *                 mbedtls_ssl_handshake().
+ *
+ * \note           When a server wants to retrieve early data, it is expected
+ *                 that this function starts the handshake for the SSL context
+ *                 \p ssl. But this is not mandatory.
+ *
+ */
+int mbedtls_ssl_read_early_data( mbedtls_ssl_context *ssl,
+                                 unsigned char *buf, size_t len );
+#endif /* MBEDTLS_SSL_SRV_C */
+
+#if defined(MBEDTLS_SSL_CLI_C)
+/**
+ * \brief          Try to write exactly 'len' application data bytes while
+ *                 performing the handshake (early data).
+ *
+ * \note           This function behaves mainly as mbedtls_ssl_write(). The
+ *                 specification of mbedtls_ssl_write() relevant to TLS 1.3
+ *                 (thus not the parts specific to (D)TLS1.2) applies to this
+ *                 function and the present documentation is restricted to the
+ *                 differences with mbedtls_ssl_write().
+ *
+ * \param ssl      SSL context
+ * \param buf      buffer holding the data
+ * \param len      how many bytes must be written
+ *
+ * \return         One additional specific return value:
+ *                 #MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA.
+ *
+ *                 #MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA is returned when it
+ *                 is not possible to write early data for the SSL context
+ *                 \p ssl.
+ *
+ *                 It may have been possible and it is not possible
+ *                 anymore because the client received the server Finished
+ *                 message, the server rejected early data or the maximum
+ *                 number of allowed early data for the PSK in use has been
+ *                 reached.
+ *
+ *                 It may never have been possible and will never be possible
+ *                 for the SSL context \p ssl because the use of early data
+ *                 is disabled for that context or more generally the context
+ *                 is not suitably configured to enable early data or the first
+ *                 call to the function was done while the handshake was
+ *                 already completed.
+ *
+ *                 It is not possible to write early data for the SSL context
+ *                 \p ssl but this does not preclude for using it with
+ *                 mbedtls_ssl_write(), mbedtls_ssl_read() or
+ *                 mbedtls_ssl_handshake().
+ *
+ * \note           This function may write early data only if the SSL context
+ *                 has been configured for the handshake with a PSK for which
+ *                 early data is allowed.
+ *
+ * \note           To maximize the number of early data that can be written in
+ *                 the course of the handshake, it is expected that this
+ *                 function starts the handshake for the SSL context \p ssl.
+ *                 But this is not mandatory.
+ *
+ * \note           This function does not provide any information on whether
+ *                 the server has accepted or will accept early data or not.
+ *                 When it returns a positive value, it just means that it
+ *                 has written early data to the server. To know whether the
+ *                 server has accepted early data or not, you should call
+ *                 mbedtls_ssl_get_early_data_status() with the handshake
+ *                 completed.
+ */
+int mbedtls_ssl_write_early_data( mbedtls_ssl_context *ssl,
+                                  const unsigned char *buf, size_t len );
+
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT  0
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED  1
+#define MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED  2
+/**
+ * \brief Get the status of the negotiation of the use of early data.
+ *
+ * \param ssl      The SSL context to query
+ *
+ * \return         #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if this function is called
+ *                 from the server-side.
+ *
+ * \return         #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if this function is called
+ *                 prior to completion of the handshake.
+ *
+ * \return         #MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT if the client has
+ *                 not indicated the use of early data to the server.
+ *
+ * \return         #MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED if the client has
+ *                 indicated the use of early data and the server has accepted
+ *                 it.
+ *
+ * \return         #MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED if the client has
+ *                 indicated the use of early data but the server has rejected
+ *                 it. In this situation, the client may want to re-send the
+ *                 early data it may have tried to send by calling
+ *                 mbedtls_ssl_write_early_data() as ordinary post-handshake
+ *                 application data by calling mbedtls_ssl_write().
+ *
+ */
+int mbedtls_ssl_get_early_data_status( mbedtls_ssl_context *ssl );
+#endif /* MBEDTLS_SSL_CLI_C */
+
+#endif /* MBEDTLS_SSL_EARLY_DATA */
+
 /**
  * \brief          Free referenced items in an SSL context and clear memory
  *
diff --git a/library/cipher.c b/library/cipher.c
index dfb7329..dffe3ad 100644
--- a/library/cipher.c
+++ b/library/cipher.c
@@ -500,7 +500,7 @@
     }
 #endif
 
-    return( 0 );
+    return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
 }
 #endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */
 
@@ -1129,7 +1129,7 @@
     }
 #endif
 
-    return( 0 );
+    return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
 }
 
 int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx,
@@ -1156,11 +1156,8 @@
     }
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
-    /* Status to return on a non-authenticated algorithm. It would make sense
-     * to return MBEDTLS_ERR_CIPHER_INVALID_CONTEXT or perhaps
-     * MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA, but at the time I write this our
-     * unit tests assume 0. */
-    ret = 0;
+    /* Status to return on a non-authenticated algorithm. */
+    ret = MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE;
 
 #if defined(MBEDTLS_GCM_C)
     if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode )
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index 9d2e69e..0109f77 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -1183,11 +1183,11 @@
             return( ret );
         p += ext_len;
 
-        /* Initializes the status to `indication sent`. It will be updated to
-         * `accepted` or `rejected` depending on whether the EncryptedExtension
-         * message will contain an early data indication extension or not.
+        /* Initializes the status to `rejected`. It will be updated to
+         * `accepted` if the EncryptedExtension message contain an early data
+         * indication extension.
          */
-        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_INDICATION_SENT;
+        ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED;
     }
     else
     {
diff --git a/tests/suites/test_suite_cipher.function b/tests/suites/test_suite_cipher.function
index b7c3b51..ff936df 100644
--- a/tests/suites/test_suite_cipher.function
+++ b/tests/suites/test_suite_cipher.function
@@ -450,8 +450,12 @@
     TEST_ASSERT( 0 == mbedtls_cipher_reset( &ctx_enc ) );
 
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
-    TEST_ASSERT( 0 == mbedtls_cipher_update_ad( &ctx_dec, ad, sizeof( ad ) - i ) );
-    TEST_ASSERT( 0 == mbedtls_cipher_update_ad( &ctx_enc, ad, sizeof( ad ) - i ) );
+    int expected = ( cipher_info->mode == MBEDTLS_MODE_GCM ||
+                     cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 ) ?
+                   0 : MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE;
+
+    TEST_EQUAL( expected, mbedtls_cipher_update_ad( &ctx_dec, ad, sizeof(ad) - i ) );
+    TEST_EQUAL( expected, mbedtls_cipher_update_ad( &ctx_enc, ad, sizeof(ad) - i ) );
 #endif
 
     block_size = mbedtls_cipher_get_block_size( &ctx_enc );
@@ -470,7 +474,7 @@
     total_len += outlen;
 
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
-    TEST_ASSERT( 0 == mbedtls_cipher_write_tag( &ctx_enc, tag, sizeof( tag ) ) );
+    TEST_EQUAL( expected, mbedtls_cipher_write_tag( &ctx_enc, tag, sizeof(tag) ) );
 #endif
 
     TEST_ASSERT( total_len == length ||
@@ -491,7 +495,7 @@
     total_len += outlen;
 
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
-    TEST_ASSERT( 0 == mbedtls_cipher_check_tag( &ctx_dec, tag, sizeof( tag ) ) );
+    TEST_EQUAL( expected, mbedtls_cipher_check_tag( &ctx_dec, tag, sizeof(tag) ) );
 #endif
 
     /* check result */
@@ -547,7 +551,11 @@
     TEST_ASSERT( 0 == mbedtls_cipher_set_iv( &ctx, iv, 16 ) );
     TEST_ASSERT( 0 == mbedtls_cipher_reset( &ctx ) );
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
-    TEST_ASSERT( 0 == mbedtls_cipher_update_ad( &ctx, NULL, 0 ) );
+    int expected = ( cipher_info->mode == MBEDTLS_MODE_GCM ||
+                     cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 ) ?
+                   0 : MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE;
+
+    TEST_EQUAL( expected, mbedtls_cipher_update_ad( &ctx, NULL, 0 ) );
 #endif
 
     /* encode length number of bytes from inbuf */
@@ -609,7 +617,11 @@
     TEST_ASSERT( 0 == mbedtls_cipher_reset( &ctx_dec ) );
 
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
-    TEST_ASSERT( 0 == mbedtls_cipher_update_ad( &ctx_dec, NULL, 0 ) );
+    int expected = ( cipher_info->mode == MBEDTLS_MODE_GCM ||
+                     cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 ) ?
+                   0 : MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE;
+
+    TEST_EQUAL( expected, mbedtls_cipher_update_ad( &ctx_dec, NULL, 0 ) );
 #endif
 
     /* decode 0-byte string */
@@ -710,8 +722,12 @@
     TEST_ASSERT( 0 == mbedtls_cipher_reset( &ctx_enc ) );
 
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
-    TEST_ASSERT( 0 == mbedtls_cipher_update_ad( &ctx_dec, NULL, 0 ) );
-    TEST_ASSERT( 0 == mbedtls_cipher_update_ad( &ctx_enc, NULL, 0 ) );
+    int expected = ( cipher_info->mode == MBEDTLS_MODE_GCM ||
+                     cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 ) ?
+                   0 : MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE;
+
+    TEST_EQUAL( expected, mbedtls_cipher_update_ad( &ctx_dec, NULL, 0 ) );
+    TEST_EQUAL( expected, mbedtls_cipher_update_ad( &ctx_enc, NULL, 0 ) );
 #endif
 
     block_size = mbedtls_cipher_get_block_size( &ctx_enc );
@@ -795,7 +811,11 @@
     TEST_ASSERT( 0 == mbedtls_cipher_set_iv( &ctx, iv->x, iv->len ) );
     TEST_ASSERT( 0 == mbedtls_cipher_reset( &ctx ) );
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
-    TEST_ASSERT( 0 == mbedtls_cipher_update_ad( &ctx, ad->x, ad->len ) );
+    int expected = ( ctx.cipher_info->mode == MBEDTLS_MODE_GCM ||
+                     ctx.cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 ) ?
+                   0 : MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE;
+
+    TEST_EQUAL( expected, mbedtls_cipher_update_ad( &ctx, ad->x, ad->len ) );
 #endif
 
     /* decode buffer and check tag->x */
@@ -806,7 +826,11 @@
                                                  &outlen ) );
     total_len += outlen;
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
-    TEST_ASSERT( tag_result == mbedtls_cipher_check_tag( &ctx, tag->x, tag->len ) );
+    int tag_expected = ( ctx.cipher_info->mode == MBEDTLS_MODE_GCM ||
+                         ctx.cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 ) ?
+                       tag_result : MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE;
+
+    TEST_EQUAL( tag_expected, mbedtls_cipher_check_tag( &ctx, tag->x, tag->len ) );
 #endif
 
     /* check plaintext only if everything went fine */