New function mbedtls_cipher_finish_padded
New function `mbedtls_cipher_finish_padded()`, similar to
`mbedtls_cipher_finish()`, but reporting padding errors through a separate
output parameter. This makes it easier to avoid leaking the presence of a
padding error, especially through timing. Thus the new function is
recommended to defend against padding oracle attacks.
In this commit, implement this function naively, with timing that depends on
whether an error happened. A subsequent commit will make this function
constant-time.
Copy the test decrypt_test_vec and decrypt_test_vec_cf test cases into
variants that call `mbedtls_cipher_finish_padded()`.
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/include/mbedtls/cipher.h b/include/mbedtls/cipher.h
index 1dc31c9..6ef703d 100644
--- a/include/mbedtls/cipher.h
+++ b/include/mbedtls/cipher.h
@@ -881,7 +881,7 @@
* 1. mbedtls_cipher_set_iv() if the mode uses an IV/nonce.
* 2. mbedtls_cipher_reset()
* 3. mbedtls_cipher_update() one or more times
- * 4. mbedtls_cipher_finish()
+ * 4. mbedtls_cipher_finish() or mbedtls_cipher_finish_padded()
* .
* This sequence can be repeated to encrypt or decrypt multiple
* messages with the same key.
@@ -892,7 +892,7 @@
* 2. mbedtls_cipher_reset()
* 3. mbedtls_cipher_update_ad()
* 4. mbedtls_cipher_update() one or more times
- * 5. mbedtls_cipher_finish()
+ * 5. mbedtls_cipher_finish() or mbedtls_cipher_finish_padded()
* 6. mbedtls_cipher_check_tag() (for decryption) or
* mbedtls_cipher_write_tag() (for encryption).
* .
@@ -930,7 +930,8 @@
* many block-sized blocks of data as possible to output.
* Any data that cannot be written immediately is either
* added to the next block, or flushed when
- * mbedtls_cipher_finish() is called.
+ * mbedtls_cipher_finish() or mbedtls_cipher_finish_padded()
+ * is called.
* Exception: For MBEDTLS_MODE_ECB, expects a single block
* in size. For example, 16 Bytes for AES.
*
@@ -964,6 +965,19 @@
* contained in it is padded to the size of
* the last block, and written to the \p output buffer.
*
+ * \warning This function reports invalid padding through an error
+ * code. Adversaries may be able to decrypt encrypted
+ * data if they can submit chosen ciphertexts and
+ * detect whether it has valid padding or not,
+ * either through direct observation or through a side
+ * channel such as timing. This is known as a
+ * padding oracle attack.
+ * Therefore applications that call this function for
+ * decryption with a cipher that involves padding
+ * should take care around error handling. Preferably,
+ * such applicatios should use
+ * mbedtls_cipher_finish_padded() instead of this function.
+ *
* \param ctx The generic cipher context. This must be initialized and
* bound to a key.
* \param output The buffer to write data to. This needs to be a writable
@@ -977,12 +991,56 @@
* \return #MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED on decryption
* expecting a full block but not receiving one.
* \return #MBEDTLS_ERR_CIPHER_INVALID_PADDING on invalid padding
- * while decrypting.
+ * while decrypting. Note that invalid-padding errors
+ * should be handled carefully; see the warning above.
* \return A cipher-specific error code on failure.
*/
int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx,
unsigned char *output, size_t *olen);
+/**
+ * \brief The generic cipher finalization function. If data still
+ * needs to be flushed from an incomplete block, the data
+ * contained in it is padded to the size of
+ * the last block, and written to the \p output buffer.
+ *
+ * \note This function is similar to mbedtls_cipher_finish().
+ * The only difference is that it reports invalid padding
+ * decryption differently, through the \p invalid_padding
+ * parameter rather than an error code.
+ * For encryption, and in modes without padding (including
+ * all authenticated modes), this function is identical
+ * to mbedtls_cipher_finish().
+ *
+ * \param[in,out] ctx The generic cipher context. This must be initialized and
+ * bound to a key.
+ * \param[out] output The buffer to write data to. This needs to be a writable
+ * buffer of at least block_size Bytes.
+ * \param[out] olen The length of the data written to the \p output buffer.
+ * This may not be \c NULL.
+ * \param[out] invalid_padding
+ * If this function returns \c 0 on decryption,
+ * \p *invalid_padding is \c 0 if the ciphertext was
+ * valid, and all-bits-one if the ciphertext had invalid
+ * padding.
+ * On encryption, or in a mode without padding (including
+ * all authenticated modes), \p *invalid_padding is \c 0
+ * on success.
+ * The value in \p *invalid_padding is unspecified if
+ * this function returns a nonzero status.
+ *
+ * \return \c 0 on success.
+ * Also \c 0 for decryption with invalid padding.
+ * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
+ * parameter-verification failure.
+ * \return #MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED on decryption
+ * expecting a full block but not receiving one.
+ * \return A cipher-specific error code on failure.
+ */
+int mbedtls_cipher_finish_padded(mbedtls_cipher_context_t *ctx,
+ unsigned char *output, size_t *olen,
+ size_t *invalid_padding);
+
#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
/**
* \brief This function writes a tag for AEAD ciphers.