Test mbedtls_ssl_decrypt_buf(): stream cipher, negative cases
Test mbedtls_ssl_decrypt_buf() with a stream cipher (RC4 or null). Test the
good case (to make sure the test code constructs the input correctly), test
with an invalid MAC, and test with a shortened input.
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/tests/suites/test_suite_ssl_decrypt.function b/tests/suites/test_suite_ssl_decrypt.function
index a7104bf..39c92ce 100644
--- a/tests/suites/test_suite_ssl_decrypt.function
+++ b/tests/suites/test_suite_ssl_decrypt.function
@@ -2,6 +2,7 @@
/* Testing of mbedtls_ssl_decrypt_buf() specifically, focusing on negative
* testing (using malformed inputs). */
+#include <mbedtls/cipher.h>
#include <mbedtls/ssl.h>
#include <test/ssl_helpers.h>
@@ -12,6 +13,128 @@
* END_DEPENDENCIES
*/
+/* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_SSL_SOME_MODES_USE_MAC */
+void ssl_decrypt_stream(int cipher_type, int hash_id, int trunc_hmac)
+{
+ mbedtls_ssl_transform transform_in, transform_out;
+ mbedtls_ssl_transform_init(&transform_in);
+ mbedtls_ssl_transform_init(&transform_out);
+ mbedtls_record rec_good = {
+ .ctr = { 0 },
+ .type = MBEDTLS_SSL_MSG_APPLICATION_DATA,
+ /* For simplicity, we only test one protocol version (TLS 1.2).
+ * For stream ciphers (unlike CBC), there are no changes in the
+ * data record format between SSL 3.0 and TLS 1.2 inclusive, so
+ * testing a single version should be good enough. */
+ .ver = { MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3 },
+ .buf = NULL,
+ .buf_len = 0,
+ .data_offset = 0,
+ .data_len = 0,
+#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
+ .cid_len = 0,
+ .cid = { 0 },
+#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
+ };
+ const char sample_plaintext[3] = "ABC";
+ mbedtls_cipher_context_t cipher;
+ mbedtls_cipher_init(&cipher);
+ mbedtls_ssl_context ssl;
+ mbedtls_ssl_init(&ssl);
+ uint8_t *buf = NULL;
+
+ USE_PSA_INIT();
+
+ TEST_EQUAL(mbedtls_test_ssl_build_transforms(&transform_in, &transform_out,
+ cipher_type, hash_id,
+ 0, trunc_hmac,
+ rec_good.ver[1],
+ 0, 0), 0);
+
+ const size_t plaintext_length = sizeof(sample_plaintext);
+ rec_good.buf_len = plaintext_length + transform_in.maclen;
+ rec_good.data_len = plaintext_length;
+ TEST_CALLOC(rec_good.buf, rec_good.buf_len);
+ memcpy(rec_good.buf, sample_plaintext, plaintext_length);
+ TEST_EQUAL(mbedtls_test_ssl_prepare_record_mac(&rec_good,
+ &transform_out), 0);
+
+ /* Encrypt in place */
+ size_t len;
+ TEST_EQUAL(mbedtls_cipher_crypt(&transform_out.cipher_ctx_enc,
+ transform_out.iv_enc, transform_out.ivlen,
+ rec_good.buf + rec_good.data_offset,
+ rec_good.data_len,
+ rec_good.buf + rec_good.data_offset,
+ &len), 0);
+ /* This function only supports stream ciphers, which should preserve
+ * the length. */
+ TEST_EQUAL(len, rec_good.data_len);
+
+ /* Good case */
+ mbedtls_record rec = rec_good;
+ TEST_EQUAL(mbedtls_ssl_decrypt_buf(&ssl, &transform_in, &rec), 0);
+
+ /* Change any one byte of the plaintext or MAC. The MAC will be wrong. */
+ TEST_CALLOC(buf, rec.buf_len);
+ for (size_t i = 0; i < rec.buf_len; i++) {
+ mbedtls_test_set_step(i);
+ rec = rec_good;
+ rec.buf = buf;
+ memcpy(buf, rec_good.buf, rec.buf_len);
+ buf[i] ^= 1;
+ TEST_EQUAL(mbedtls_ssl_decrypt_buf(&ssl, &transform_in, &rec),
+ MBEDTLS_ERR_SSL_INVALID_MAC);
+ }
+ mbedtls_free(buf);
+ buf = NULL;
+
+ /* Shorter input buffer. Either the MAC will be wrong, or there isn't
+ * enough room for a MAC. */
+ for (size_t n = 1; n < rec.buf_len; n++) {
+ mbedtls_test_set_step(n);
+ rec = rec_good;
+ TEST_CALLOC(buf, n);
+ rec.buf = buf;
+ rec.buf_len = n;
+ rec.data_len = n;
+ memcpy(buf, rec_good.buf, n);
+ TEST_EQUAL(mbedtls_ssl_decrypt_buf(&ssl, &transform_in, &rec),
+ MBEDTLS_ERR_SSL_INVALID_MAC);
+ mbedtls_free(buf);
+ buf = NULL;
+ }
+
+ /* For robustness, check a 0-length buffer (non-null, then null).
+ * This should not reach mbedtls_ssl_decrypt_buf() as used in the library,
+ * so the exact error doesn't matter, but we don't want a crash. */
+ {
+ const uint8_t buf1[1] = { 'a' };
+ rec = rec_good;
+ /* We won't write to buf1[0] since it's out of range, so we can cast
+ * the const away. */
+ rec.buf = (uint8_t *) buf1;
+ rec.buf_len = 0;
+ TEST_EQUAL(mbedtls_ssl_decrypt_buf(&ssl, &transform_in, &rec),
+ MBEDTLS_ERR_SSL_INTERNAL_ERROR);
+ }
+ rec = rec_good;
+ rec.buf = NULL;
+ rec.buf_len = 0;
+ TEST_EQUAL(mbedtls_ssl_decrypt_buf(&ssl, &transform_in, &rec),
+ MBEDTLS_ERR_SSL_INTERNAL_ERROR);
+
+exit:
+ USE_PSA_DONE();
+ mbedtls_ssl_transform_free(&transform_in);
+ mbedtls_ssl_transform_free(&transform_out);
+ mbedtls_free(rec_good.buf);
+ mbedtls_ssl_free(&ssl);
+ mbedtls_cipher_free(&cipher);
+ mbedtls_free(buf);
+}
+/* END_CASE */
+
/* BEGIN_CASE depends_on:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_AES_C:MBEDTLS_SSL_PROTO_TLS1_2 */
void ssl_decrypt_non_etm_cbc(int cipher_type, int hash_id, int trunc_hmac,
int length_selector)
diff --git a/tests/suites/test_suite_ssl_decrypt.misc.data b/tests/suites/test_suite_ssl_decrypt.misc.data
index c2e543e..770ca6f 100644
--- a/tests/suites/test_suite_ssl_decrypt.misc.data
+++ b/tests/suites/test_suite_ssl_decrypt.misc.data
@@ -1,3 +1,67 @@
+Decrypt null cipher, MD5
+depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_MD5_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_MD5:0
+
+Decrypt null cipher, MD5 trunc
+depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_MD5_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_MD5:1
+
+Decrypt null cipher, SHA-1
+depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_SHA1_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_SHA1:0
+
+Decrypt null cipher, SHA-1 trunc
+depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_SHA1_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_SHA1:1
+
+Decrypt null cipher, SHA-256
+depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_SHA256_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_SHA256:0
+
+Decrypt null cipher, SHA-256 trunc
+depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_SHA256_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_SHA256:1
+
+Decrypt null cipher, SHA-384
+depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_SHA512_C:!MBEDTLS_SHA512_NO_SHA384
+ssl_decrypt_stream:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_SHA384:0
+
+Decrypt null cipher, SHA-384 trunc
+depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_SHA512_C:!MBEDTLS_SHA512_NO_SHA384
+ssl_decrypt_stream:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_SHA384:1
+
+Decrypt RC4, MD5
+depends_on:MBEDTLS_ARC4_C:MBEDTLS_MD5_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_ARC4_128:MBEDTLS_MD_MD5:0
+
+Decrypt RC4, MD5 trunc
+depends_on:MBEDTLS_ARC4_C:MBEDTLS_MD5_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_ARC4_128:MBEDTLS_MD_MD5:1
+
+Decrypt RC4, SHA-1
+depends_on:MBEDTLS_ARC4_C:MBEDTLS_SHA1_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_ARC4_128:MBEDTLS_MD_SHA1:0
+
+Decrypt RC4, SHA-1 trunc
+depends_on:MBEDTLS_ARC4_C:MBEDTLS_SHA1_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_ARC4_128:MBEDTLS_MD_SHA1:1
+
+Decrypt RC4, SHA-256
+depends_on:MBEDTLS_ARC4_C:MBEDTLS_SHA256_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_ARC4_128:MBEDTLS_MD_SHA256:0
+
+Decrypt RC4, SHA-256 trunc
+depends_on:MBEDTLS_ARC4_C:MBEDTLS_SHA256_C
+ssl_decrypt_stream:MBEDTLS_CIPHER_ARC4_128:MBEDTLS_MD_SHA256:1
+
+Decrypt RC4, SHA-384
+depends_on:MBEDTLS_ARC4_C:MBEDTLS_SHA512_C:!MBEDTLS_SHA512_NO_SHA384
+ssl_decrypt_stream:MBEDTLS_CIPHER_ARC4_128:MBEDTLS_MD_SHA384:0
+
+Decrypt RC4, SHA-384 trunc
+depends_on:MBEDTLS_ARC4_C:MBEDTLS_SHA512_C:!MBEDTLS_SHA512_NO_SHA384
+ssl_decrypt_stream:MBEDTLS_CIPHER_ARC4_128:MBEDTLS_MD_SHA384:1
+
Decrypt CBC !EtM, AES MD5 !trunc, empty plaintext, minpad
depends_on:MBEDTLS_AES_C:MBEDTLS_MD5_C
ssl_decrypt_non_etm_cbc:MBEDTLS_CIPHER_AES_128_CBC:MBEDTLS_MD_MD5:0:-1