Merge pull request #4014 from hanno-arm/mps_reader
Add MPS reader component
diff --git a/docs/architecture/tls13-experimental.md b/docs/architecture/tls13-experimental.md
index 3db16e0..10cbfa1 100644
--- a/docs/architecture/tls13-experimental.md
+++ b/docs/architecture/tls13-experimental.md
@@ -47,3 +47,22 @@
Those functions are implemented in `library/ssl_tls13_keys.c` and
tested in `test_suite_ssl` using test vectors from RFC 8448 and
https://tls13.ulfheim.net/.
+
+- New TLS Message Processing Stack (MPS)
+
+ The TLS 1.3 prototype is developed alongside a rewrite of the TLS messaging layer,
+ encompassing low-level details such as record parsing, handshake reassembly, and
+ DTLS retransmission state machine.
+
+ MPS has the following components:
+ - Layer 1 (Datagram handling)
+ - Layer 2 (Record handling)
+ - Layer 3 (Message handling)
+ - Layer 4 (Retransmission State Machine)
+ - Reader (Abstracted pointer arithmetic and reassembly logic for incoming data)
+ - Writer (Abstracted pointer arithmetic and fragmentation logic for outgoing data)
+
+ Of those components, the following have been upstreamed
+ as part of `MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL`:
+
+ - Reader ([`library/mps_reader.h`](../../library/mps_reader.h))
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index cff4cf9..220fbf9 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -47,6 +47,8 @@
md4.c
md5.c
memory_buffer_alloc.c
+ mps_reader.c
+ mps_trace.c
nist_kw.c
oid.c
padlock.c
diff --git a/library/Makefile b/library/Makefile
index 55af96e..13b0b29 100644
--- a/library/Makefile
+++ b/library/Makefile
@@ -104,6 +104,8 @@
md4.o \
md5.o \
memory_buffer_alloc.o \
+ mps_reader.o \
+ mps_trace.o \
nist_kw.o \
oid.o \
padlock.o \
diff --git a/library/mps_common.h b/library/mps_common.h
new file mode 100644
index 0000000..dd6e31b
--- /dev/null
+++ b/library/mps_common.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+/**
+ * \file mps_common.h
+ *
+ * \brief Common functions and macros used by MPS
+ */
+
+#ifndef MBEDTLS_MPS_COMMON_H
+#define MBEDTLS_MPS_COMMON_H
+
+#include "mps_error.h"
+
+#include <stdio.h>
+
+/**
+ * \name SECTION: MPS Configuration
+ *
+ * \{
+ */
+
+/*! This flag controls whether the MPS-internal components
+ * (reader, writer, Layer 1-3) perform validation of the
+ * expected abstract state at the entry of API calls.
+ *
+ * Context: All MPS API functions impose assumptions/preconditions on the
+ * context on which they operate. For example, every structure has a notion of
+ * state integrity which is established by `xxx_init()` and preserved by any
+ * calls to the MPS API which satisfy their preconditions and either succeed,
+ * or fail with an error code which is explicitly documented to not corrupt
+ * structure integrity (such as WANT_READ and WANT_WRITE);
+ * apart from `xxx_init()` any function assumes state integrity as a
+ * precondition (but usually more). If any of the preconditions is violated,
+ * the function's behavior is entirely undefined.
+ * In addition to state integrity, all MPS structures have a more refined
+ * notion of abstract state that the API operates on. For example, all layers
+ * have a notion of 'abtract read state' which indicates if incoming data has
+ * been passed to the user, e.g. through mps_l2_read_start() for Layer 2
+ * or mps_l3_read() in Layer 3. After such a call, it doesn't make sense to
+ * call these reading functions again until the incoming data has been
+ * explicitly 'consumed', e.g. through mps_l2_read_consume() for Layer 2 or
+ * mps_l3_read_consume() on Layer 3. However, even if it doesn't make sense,
+ * it's a design choice whether the API should fail gracefully on such
+ * non-sensical calls or not, and that's what this option is about:
+ *
+ * This option determines whether the expected abstract state
+ * is part of the API preconditions or not: If the option is set,
+ * then the abstract state is not part of the precondition and is
+ * thus required to be validated by the implementation. If an unexpected
+ * abstract state is encountered, the implementation must fail gracefully
+ * with error #MBEDTLS_ERR_MPS_OPERATION_UNEXPECTED.
+ * Conversely, if this option is not set, then the expected abstract state
+ * is included in the preconditions of the respective API calls, and
+ * an implementation's behaviour is undefined if the abstract state is
+ * not as expected.
+ *
+ * For example: Enabling this makes mps_l2_read_done() fail if
+ * no incoming record is currently open; disabling this would
+ * lead to undefined behavior in this case.
+ *
+ * Comment this to remove state validation.
+ */
+#define MBEDTLS_MPS_STATE_VALIDATION
+
+/*! This flag enables/disables assertions on the internal state of MPS.
+ *
+ * Assertions are sanity checks that should never trigger when MPS
+ * is used within the bounds of its API and preconditions.
+ *
+ * Enabling this increases security by limiting the scope of
+ * potential bugs, but comes at the cost of increased code size.
+ *
+ * Note: So far, there is no guiding principle as to what
+ * expected conditions merit an assertion, and which don't.
+ *
+ * Comment this to disable assertions.
+ */
+#define MBEDTLS_MPS_ENABLE_ASSERTIONS
+
+/*! This flag controls whether tracing for MPS should be enabled. */
+//#define MBEDTLS_MPS_ENABLE_TRACE
+
+#if defined(MBEDTLS_MPS_STATE_VALIDATION)
+
+#define MBEDTLS_MPS_STATE_VALIDATE_RAW( cond, string ) \
+ do \
+ { \
+ if( !(cond) ) \
+ { \
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error, string ); \
+ MBEDTLS_MPS_TRACE_RETURN( MBEDTLS_ERR_MPS_OPERATION_UNEXPECTED ); \
+ } \
+ } while( 0 )
+
+#else /* MBEDTLS_MPS_STATE_VALIDATION */
+
+#define MBEDTLS_MPS_STATE_VALIDATE_RAW( cond, string ) \
+ do \
+ { \
+ ( cond ); \
+ } while( 0 )
+
+#endif /* MBEDTLS_MPS_STATE_VALIDATION */
+
+#if defined(MBEDTLS_MPS_ENABLE_ASSERTIONS)
+
+#define MBEDTLS_MPS_ASSERT_RAW( cond, string ) \
+ do \
+ { \
+ if( !(cond) ) \
+ { \
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error, string ); \
+ MBEDTLS_MPS_TRACE_RETURN( MBEDTLS_ERR_MPS_INTERNAL_ERROR ); \
+ } \
+ } while( 0 )
+
+#else /* MBEDTLS_MPS_ENABLE_ASSERTIONS */
+
+#define MBEDTLS_MPS_ASSERT_RAW( cond, string ) do {} while( 0 )
+
+#endif /* MBEDTLS_MPS_ENABLE_ASSERTIONS */
+
+
+/* \} name SECTION: MPS Configuration */
+
+/**
+ * \name SECTION: Common types
+ *
+ * Various common types used throughout MPS.
+ * \{
+ */
+
+/** \brief The type of buffer sizes and offsets used in MPS structures.
+ *
+ * This is an unsigned integer type that should be large enough to
+ * hold the length of any buffer or message processed by MPS.
+ *
+ * The reason to pick a value as small as possible here is
+ * to reduce the size of MPS structures.
+ *
+ * \warning Care has to be taken when using a narrower type
+ * than ::mbedtls_mps_size_t here because of
+ * potential truncation during conversion.
+ *
+ * \warning Handshake messages in TLS may be up to 2^24 ~ 16Mb in size.
+ * If mbedtls_mps_[opt_]stored_size_t is smaller than that, the
+ * maximum handshake message is restricted accordingly.
+ *
+ * For now, we use the default type of size_t throughout, and the use of
+ * smaller types or different types for ::mbedtls_mps_size_t and
+ * ::mbedtls_mps_stored_size_t is not yet supported.
+ *
+ */
+typedef size_t mbedtls_mps_stored_size_t;
+#define MBEDTLS_MPS_STORED_SIZE_MAX ( (mbedtls_mps_stored_size_t) -1 )
+
+/** \brief The type of buffer sizes and offsets used in the MPS API
+ * and implementation.
+ *
+ * This must be at least as wide as ::mbedtls_stored_size_t but
+ * may be chosen to be strictly larger if more suitable for the
+ * target architecture.
+ *
+ * For example, in a test build for ARM Thumb, using uint_fast16_t
+ * instead of uint16_t reduced the code size from 1060 Byte to 962 Byte,
+ * so almost 10%.
+ */
+typedef size_t mbedtls_mps_size_t;
+#define MBEDTLS_MPS_SIZE_MAX ( (mbedtls_mps_size_t) -1 )
+
+#if MBEDTLS_MPS_STORED_SIZE_MAX > MBEDTLS_MPS_SIZE_MAX
+#error "Misconfiguration of mbedtls_mps_size_t and mbedtls_mps_stored_size_t."
+#endif
+
+/* \} SECTION: Common types */
+
+
+#endif /* MBEDTLS_MPS_COMMON_H */
diff --git a/library/mps_error.h b/library/mps_error.h
new file mode 100644
index 0000000..f78d9a0
--- /dev/null
+++ b/library/mps_error.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+/**
+ * \file mps_error.h
+ *
+ * \brief Error codes used by MPS
+ */
+
+#ifndef MBEDTLS_MPS_ERROR_H
+#define MBEDTLS_MPS_ERROR_H
+
+
+/* TODO: The error code allocation needs to be revisited:
+ *
+ * - Should we make (some of) the MPS Reader error codes public?
+ * If so, we need to adjust MBEDTLS_MPS_READER_MAKE_ERROR() to hit
+ * a gap in the Mbed TLS public error space.
+ * If not, we have to make sure we don't forward those errors
+ * at the level of the public API -- no risk at the moment as
+ * long as MPS is an experimental component not accessible from
+ * public API.
+ */
+
+/**
+ * \name SECTION: MPS general error codes
+ *
+ * \{
+ */
+
+#ifndef MBEDTLS_MPS_ERR_BASE
+#define MBEDTLS_MPS_ERR_BASE ( 0 )
+#endif
+
+#define MBEDTLS_MPS_MAKE_ERROR(code) \
+ ( -( MBEDTLS_MPS_ERR_BASE | (code) ) )
+
+#define MBEDTLS_ERR_MPS_OPERATION_UNEXPECTED MBEDTLS_MPS_MAKE_ERROR( 0x1 )
+#define MBEDTLS_ERR_MPS_INTERNAL_ERROR MBEDTLS_MPS_MAKE_ERROR( 0x2 )
+
+/* \} name SECTION: MPS general error codes */
+
+/**
+ * \name SECTION: MPS Reader error codes
+ *
+ * \{
+ */
+
+#ifndef MBEDTLS_MPS_READER_ERR_BASE
+#define MBEDTLS_MPS_READER_ERR_BASE ( 1 << 8 )
+#endif
+
+#define MBEDTLS_MPS_READER_MAKE_ERROR(code) \
+ ( -( MBEDTLS_MPS_READER_ERR_BASE | (code) ) )
+
+/*! An attempt to reclaim the data buffer from a reader failed because
+ * the user hasn't yet read and committed all of it. */
+#define MBEDTLS_ERR_MPS_READER_DATA_LEFT MBEDTLS_MPS_READER_MAKE_ERROR( 0x1 )
+
+/*! An invalid argument was passed to the reader. */
+#define MBEDTLS_ERR_MPS_READER_INVALID_ARG MBEDTLS_MPS_READER_MAKE_ERROR( 0x2 )
+
+/*! An attempt to move a reader to consuming mode through mbedtls_mps_reader_feed()
+ * after pausing failed because the provided data is not sufficient to serve the
+ * read requests that led to the pausing. */
+#define MBEDTLS_ERR_MPS_READER_NEED_MORE MBEDTLS_MPS_READER_MAKE_ERROR( 0x3 )
+
+/*! A get request failed because not enough data is available in the reader. */
+#define MBEDTLS_ERR_MPS_READER_OUT_OF_DATA MBEDTLS_MPS_READER_MAKE_ERROR( 0x4 )
+
+/*!< A get request after pausing and reactivating the reader failed because
+ * the request is not in line with the request made prior to pausing. The user
+ * must not change it's 'strategy' after pausing and reactivating a reader. */
+#define MBEDTLS_ERR_MPS_READER_INCONSISTENT_REQUESTS MBEDTLS_MPS_READER_MAKE_ERROR( 0x5 )
+
+/*! An attempt to reclaim the data buffer from a reader failed because the reader
+ * has no accumulator it can use to backup the data that hasn't been processed. */
+#define MBEDTLS_ERR_MPS_READER_NEED_ACCUMULATOR MBEDTLS_MPS_READER_MAKE_ERROR( 0x6 )
+
+/*! An attempt to reclaim the data buffer from a reader failed because the
+ * accumulator passed to the reader is not large enough to hold both the
+ * data that hasn't been processed and the excess of the last read-request. */
+#define MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL MBEDTLS_MPS_READER_MAKE_ERROR( 0x7 )
+
+/* \} name SECTION: MPS Reader error codes */
+
+#endif /* MBEDTLS_MPS_ERROR_H */
diff --git a/library/mps_reader.c b/library/mps_reader.c
new file mode 100644
index 0000000..848634d
--- /dev/null
+++ b/library/mps_reader.c
@@ -0,0 +1,564 @@
+/*
+ * Message Processing Stack, Reader implementation
+ *
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of Mbed TLS (https://tls.mbed.org)
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL)
+
+#include "mps_reader.h"
+#include "mps_common.h"
+#include "mps_trace.h"
+
+#include <string.h>
+
+#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
+ !defined(inline) && !defined(__cplusplus)
+#define inline __inline
+#endif
+
+#if defined(MBEDTLS_MPS_ENABLE_TRACE)
+static int mbedtls_mps_trace_id = MBEDTLS_MPS_TRACE_BIT_READER;
+#endif /* MBEDTLS_MPS_ENABLE_TRACE */
+
+/*
+ * GENERAL NOTE ON CODING STYLE
+ *
+ * The following code intentionally separates memory loads
+ * and stores from other operations (arithmetic or branches).
+ * This leads to the introduction of many local variables
+ * and significantly increases the C-code line count, but
+ * should not increase the size of generated assembly.
+ *
+ * The reason for this is twofold:
+ * (1) It will ease verification efforts using the VST
+ * (Verified Software Toolchain)
+ * whose program logic cannot directly reason
+ * about instructions containing a load or store in
+ * addition to other operations (e.g. *p = *q or
+ * tmp = *p + 42).
+ * (2) Operating on local variables and writing the results
+ * back to the target contexts on success only
+ * allows to maintain structure invariants even
+ * on failure - this in turn has two benefits:
+ * (2.a) If for some reason an error code is not caught
+ * and operation continues, functions are nonetheless
+ * called with sane contexts, reducing the risk
+ * of dangerous behavior.
+ * (2.b) Randomized testing is easier if structures
+ * remain intact even in the face of failing
+ * and/or non-sensical calls.
+ * Moreover, it might even reduce code-size because
+ * the compiler need not write back temporary results
+ * to memory in case of failure.
+ *
+ */
+
+static inline int mps_reader_is_accumulating(
+ mbedtls_mps_reader const *rd )
+{
+ mbedtls_mps_size_t acc_remaining;
+ if( rd->acc == NULL )
+ return( 0 );
+
+ acc_remaining = rd->acc_share.acc_remaining;
+ return( acc_remaining > 0 );
+}
+
+static inline int mps_reader_is_producing(
+ mbedtls_mps_reader const *rd )
+{
+ unsigned char *frag = rd->frag;
+ return( frag == NULL );
+}
+
+static inline int mps_reader_is_consuming(
+ mbedtls_mps_reader const *rd )
+{
+ return( !mps_reader_is_producing( rd ) );
+}
+
+static inline mbedtls_mps_size_t mps_reader_get_fragment_offset(
+ mbedtls_mps_reader const *rd )
+{
+ unsigned char *acc = rd->acc;
+ mbedtls_mps_size_t frag_offset;
+
+ if( acc == NULL )
+ return( 0 );
+
+ frag_offset = rd->acc_share.frag_offset;
+ return( frag_offset );
+}
+
+static inline mbedtls_mps_size_t mps_reader_serving_from_accumulator(
+ mbedtls_mps_reader const *rd )
+{
+ mbedtls_mps_size_t frag_offset, end;
+
+ frag_offset = mps_reader_get_fragment_offset( rd );
+ end = rd->end;
+
+ return( end < frag_offset );
+}
+
+static inline void mps_reader_zero( mbedtls_mps_reader *rd )
+{
+ /* A plain memset() would likely be more efficient,
+ * but the current way of zeroing makes it harder
+ * to overlook fields which should not be zero-initialized.
+ * It's also more suitable for FV efforts since it
+ * doesn't require reasoning about structs being
+ * interpreted as unstructured binary blobs. */
+ static mbedtls_mps_reader const zero =
+ { .frag = NULL,
+ .frag_len = 0,
+ .commit = 0,
+ .end = 0,
+ .pending = 0,
+ .acc = NULL,
+ .acc_len = 0,
+ .acc_available = 0,
+ .acc_share = { .acc_remaining = 0 }
+ };
+ *rd = zero;
+}
+
+int mbedtls_mps_reader_init( mbedtls_mps_reader *rd,
+ unsigned char *acc,
+ mbedtls_mps_size_t acc_len )
+{
+ MBEDTLS_MPS_TRACE_INIT( "mbedtls_mps_reader_init" );
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "* Accumulator size: %u bytes", (unsigned) acc_len );
+ mps_reader_zero( rd );
+ rd->acc = acc;
+ rd->acc_len = acc_len;
+ MBEDTLS_MPS_TRACE_RETURN( 0 );
+}
+
+int mbedtls_mps_reader_free( mbedtls_mps_reader *rd )
+{
+ MBEDTLS_MPS_TRACE_INIT( "mbedtls_mps_reader_free" );
+ mps_reader_zero( rd );
+ MBEDTLS_MPS_TRACE_RETURN( 0 );
+}
+
+int mbedtls_mps_reader_feed( mbedtls_mps_reader *rd,
+ unsigned char *new_frag,
+ mbedtls_mps_size_t new_frag_len )
+{
+ mbedtls_mps_size_t copy_to_acc;
+ MBEDTLS_MPS_TRACE_INIT( "mbedtls_mps_reader_feed" );
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "* Fragment length: %u bytes", (unsigned) new_frag_len );
+
+ if( new_frag == NULL )
+ MBEDTLS_MPS_TRACE_RETURN( MBEDTLS_ERR_MPS_READER_INVALID_ARG );
+
+ MBEDTLS_MPS_STATE_VALIDATE_RAW( mps_reader_is_producing( rd ),
+ "mbedtls_mps_reader_feed() requires reader to be in producing mode" );
+
+ if( mps_reader_is_accumulating( rd ) )
+ {
+ unsigned char *acc = rd->acc;
+ mbedtls_mps_size_t acc_remaining = rd->acc_share.acc_remaining;
+ mbedtls_mps_size_t acc_available = rd->acc_available;
+
+ /* Skip over parts of the accumulator that have already been filled. */
+ acc += acc_available;
+
+ copy_to_acc = acc_remaining;
+ if( copy_to_acc > new_frag_len )
+ copy_to_acc = new_frag_len;
+
+ /* Copy new contents to accumulator. */
+ memcpy( acc, new_frag, copy_to_acc );
+
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "Copy new data of size %u of %u into accumulator at offset %u",
+ (unsigned) copy_to_acc, (unsigned) new_frag_len, (unsigned) acc_available );
+
+ /* Check if, with the new fragment, we have enough data. */
+ acc_remaining -= copy_to_acc;
+ if( acc_remaining > 0 )
+ {
+ /* We need to accumulate more data. Stay in producing mode. */
+ acc_available += copy_to_acc;
+ rd->acc_share.acc_remaining = acc_remaining;
+ rd->acc_available = acc_available;
+ MBEDTLS_MPS_TRACE_RETURN( MBEDTLS_ERR_MPS_READER_NEED_MORE );
+ }
+
+ /* We have filled the accumulator: Move to consuming mode. */
+
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "Enough data available to serve user request" );
+
+ /* Remember overlap of accumulator and fragment. */
+ rd->acc_share.frag_offset = acc_available;
+ acc_available += copy_to_acc;
+ rd->acc_available = acc_available;
+ }
+ else /* Not accumulating */
+ {
+ rd->acc_share.frag_offset = 0;
+ }
+
+ rd->frag = new_frag;
+ rd->frag_len = new_frag_len;
+ rd->commit = 0;
+ rd->end = 0;
+ MBEDTLS_MPS_TRACE_RETURN( 0 );
+}
+
+
+int mbedtls_mps_reader_get( mbedtls_mps_reader *rd,
+ mbedtls_mps_size_t desired,
+ unsigned char **buffer,
+ mbedtls_mps_size_t *buflen )
+{
+ unsigned char *frag;
+ mbedtls_mps_size_t frag_len, frag_offset, end, frag_fetched, frag_remaining;
+ MBEDTLS_MPS_TRACE_INIT( "mbedtls_mps_reader_get" );
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "* Bytes requested: %u", (unsigned) desired );
+
+ MBEDTLS_MPS_STATE_VALIDATE_RAW( mps_reader_is_consuming( rd ),
+ "mbedtls_mps_reader_get() requires reader to be in consuming mode" );
+
+ end = rd->end;
+ frag_offset = mps_reader_get_fragment_offset( rd );
+
+ /* Check if we're still serving from the accumulator. */
+ if( mps_reader_serving_from_accumulator( rd ) )
+ {
+ /* Illustration of supported and unsupported cases:
+ *
+ * - Allowed #1
+ *
+ * +-----------------------------------+
+ * | frag |
+ * +-----------------------------------+
+ *
+ * end end+desired
+ * | |
+ * +-----v-------v-------------+
+ * | acc |
+ * +---------------------------+
+ * | |
+ * frag_offset acc_available
+ *
+ * - Allowed #2
+ *
+ * +-----------------------------------+
+ * | frag |
+ * +-----------------------------------+
+ *
+ * end end+desired
+ * | |
+ * +----------v----------------v
+ * | acc |
+ * +---------------------------+
+ * | |
+ * frag_offset acc_available
+ *
+ * - Not allowed #1 (could be served, but we don't actually use it):
+ *
+ * +-----------------------------------+
+ * | frag |
+ * +-----------------------------------+
+ *
+ * end end+desired
+ * | |
+ * +------v-------------v------+
+ * | acc |
+ * +---------------------------+
+ * | |
+ * frag_offset acc_available
+ *
+ *
+ * - Not allowed #2 (can't be served with a contiguous buffer):
+ *
+ * +-----------------------------------+
+ * | frag |
+ * +-----------------------------------+
+ *
+ * end end + desired
+ * | |
+ * +------v--------------------+ v
+ * | acc |
+ * +---------------------------+
+ * | |
+ * frag_offset acc_available
+ *
+ * In case of Allowed #2 we're switching to serve from
+ * `frag` starting from the next call to mbedtls_mps_reader_get().
+ */
+
+ unsigned char *acc;
+
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "Serve the request from the accumulator" );
+ if( frag_offset - end < desired )
+ {
+ mbedtls_mps_size_t acc_available;
+ acc_available = rd->acc_available;
+ if( acc_available - end != desired )
+ {
+ /* It might be possible to serve some of these situations by
+ * making additional space in the accumulator, removing those
+ * parts that have already been committed.
+ * On the other hand, this brings additional complexity and
+ * enlarges the code size, while there doesn't seem to be a use
+ * case where we don't attempt exactly the same `get` calls when
+ * resuming on a reader than what we tried before pausing it.
+ * If we believe we adhere to this restricted usage throughout
+ * the library, this check is a good opportunity to
+ * validate this. */
+ MBEDTLS_MPS_TRACE_RETURN(
+ MBEDTLS_ERR_MPS_READER_INCONSISTENT_REQUESTS );
+ }
+ }
+
+ acc = rd->acc;
+ acc += end;
+
+ *buffer = acc;
+ if( buflen != NULL )
+ *buflen = desired;
+
+ end += desired;
+ rd->end = end;
+ rd->pending = 0;
+
+ MBEDTLS_MPS_TRACE_RETURN( 0 );
+ }
+
+ /* Attempt to serve the request from the current fragment */
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "Serve the request from the current fragment." );
+
+ frag_len = rd->frag_len;
+ frag_fetched = end - frag_offset; /* The amount of data from the current
+ * fragment that has already been passed
+ * to the user. */
+ frag_remaining = frag_len - frag_fetched; /* Remaining data in fragment */
+
+ /* Check if we can serve the read request from the fragment. */
+ if( frag_remaining < desired )
+ {
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "There's not enough data in the current fragment "
+ "to serve the request." );
+ /* There's not enough data in the current fragment,
+ * so either just RETURN what we have or fail. */
+ if( buflen == NULL )
+ {
+ if( frag_remaining > 0 )
+ {
+ rd->pending = desired - frag_remaining;
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "Remember to collect %u bytes before re-opening",
+ (unsigned) rd->pending );
+ }
+ MBEDTLS_MPS_TRACE_RETURN( MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+ }
+
+ desired = frag_remaining;
+ }
+
+ /* There's enough data in the current fragment to serve the
+ * (potentially modified) read request. */
+
+ frag = rd->frag;
+ frag += frag_fetched;
+
+ *buffer = frag;
+ if( buflen != NULL )
+ *buflen = desired;
+
+ end += desired;
+ rd->end = end;
+ rd->pending = 0;
+ MBEDTLS_MPS_TRACE_RETURN( 0 );
+}
+
+int mbedtls_mps_reader_commit( mbedtls_mps_reader *rd )
+{
+ mbedtls_mps_size_t end;
+ MBEDTLS_MPS_TRACE_INIT( "mbedtls_mps_reader_commit" );
+ MBEDTLS_MPS_STATE_VALIDATE_RAW( mps_reader_is_consuming( rd ),
+ "mbedtls_mps_reader_commit() requires reader to be in consuming mode" );
+
+ end = rd->end;
+ rd->commit = end;
+
+ MBEDTLS_MPS_TRACE_RETURN( 0 );
+}
+
+int mbedtls_mps_reader_reclaim( mbedtls_mps_reader *rd,
+ int *paused )
+{
+ unsigned char *frag, *acc;
+ mbedtls_mps_size_t pending, commit;
+ mbedtls_mps_size_t acc_len, frag_offset, frag_len;
+ MBEDTLS_MPS_TRACE_INIT( "mbedtls_mps_reader_reclaim" );
+
+ if( paused != NULL )
+ *paused = 0;
+
+ MBEDTLS_MPS_STATE_VALIDATE_RAW( mps_reader_is_consuming( rd ),
+ "mbedtls_mps_reader_reclaim() requires reader to be in consuming mode" );
+
+ frag = rd->frag;
+ acc = rd->acc;
+ pending = rd->pending;
+ commit = rd->commit;
+ frag_len = rd->frag_len;
+
+ frag_offset = mps_reader_get_fragment_offset( rd );
+
+ if( pending == 0 )
+ {
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "No unsatisfied read-request has been logged." );
+
+ /* Check if there's data left to be consumed. */
+ if( commit < frag_offset || commit - frag_offset < frag_len )
+ {
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "There is data left to be consumed." );
+ rd->end = commit;
+ MBEDTLS_MPS_TRACE_RETURN( MBEDTLS_ERR_MPS_READER_DATA_LEFT );
+ }
+
+ rd->acc_available = 0;
+ rd->acc_share.acc_remaining = 0;
+
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "Fragment has been fully processed and committed." );
+ }
+ else
+ {
+ int overflow;
+
+ mbedtls_mps_size_t acc_backup_offset;
+ mbedtls_mps_size_t acc_backup_len;
+ mbedtls_mps_size_t frag_backup_offset;
+ mbedtls_mps_size_t frag_backup_len;
+
+ mbedtls_mps_size_t backup_len;
+ mbedtls_mps_size_t acc_len_needed;
+
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "There has been an unsatisfied read with %u bytes overhead.",
+ (unsigned) pending );
+
+ if( acc == NULL )
+ {
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "No accumulator present" );
+ MBEDTLS_MPS_TRACE_RETURN(
+ MBEDTLS_ERR_MPS_READER_NEED_ACCUMULATOR );
+ }
+ acc_len = rd->acc_len;
+
+ /* Check if the upper layer has already fetched
+ * and committed the contents of the accumulator. */
+ if( commit < frag_offset )
+ {
+ /* No, accumulator is still being processed. */
+ frag_backup_offset = 0;
+ frag_backup_len = frag_len;
+ acc_backup_offset = commit;
+ acc_backup_len = frag_offset - commit;
+ }
+ else
+ {
+ /* Yes, the accumulator is already processed. */
+ frag_backup_offset = commit - frag_offset;
+ frag_backup_len = frag_len - frag_backup_offset;
+ acc_backup_offset = 0;
+ acc_backup_len = 0;
+ }
+
+ backup_len = acc_backup_len + frag_backup_len;
+ acc_len_needed = backup_len + pending;
+
+ overflow = 0;
+ overflow |= ( backup_len < acc_backup_len );
+ overflow |= ( acc_len_needed < backup_len );
+
+ if( overflow || acc_len < acc_len_needed )
+ {
+ /* Except for the different return code, we behave as if
+ * there hadn't been a call to mbedtls_mps_reader_get()
+ * since the last commit. */
+ rd->end = commit;
+ rd->pending = 0;
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
+ "The accumulator is too small to handle the backup." );
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
+ "* Size: %u", (unsigned) acc_len );
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
+ "* Needed: %u (%u + %u)",
+ (unsigned) acc_len_needed,
+ (unsigned) backup_len, (unsigned) pending );
+ MBEDTLS_MPS_TRACE_RETURN(
+ MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
+ }
+
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "Fragment backup: %u", (unsigned) frag_backup_len );
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "Accumulator backup: %u", (unsigned) acc_backup_len );
+
+ /* Move uncommitted parts from the accumulator to the front
+ * of the accumulator. */
+ memmove( acc, acc + acc_backup_offset, acc_backup_len );
+
+ /* Copy uncmmitted parts of the current fragment to the
+ * accumulator. */
+ memcpy( acc + acc_backup_len,
+ frag + frag_backup_offset, frag_backup_len );
+
+ rd->acc_available = backup_len;
+ rd->acc_share.acc_remaining = pending;
+
+ if( paused != NULL )
+ *paused = 1;
+ }
+
+ rd->frag = NULL;
+ rd->frag_len = 0;
+
+ rd->commit = 0;
+ rd->end = 0;
+ rd->pending = 0;
+
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
+ "Final state: aa %u, al %u, ar %u",
+ (unsigned) rd->acc_available, (unsigned) rd->acc_len,
+ (unsigned) rd->acc_share.acc_remaining );
+ MBEDTLS_MPS_TRACE_RETURN( 0 );
+}
+
+#endif /* MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL */
diff --git a/library/mps_reader.h b/library/mps_reader.h
new file mode 100644
index 0000000..427c1bd
--- /dev/null
+++ b/library/mps_reader.h
@@ -0,0 +1,382 @@
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+/**
+ * \file mps_reader.h
+ *
+ * \brief This file defines reader objects, which together with their
+ * sibling writer objects form the basis for the communication
+ * between the various layers of the Mbed TLS messaging stack,
+ * as well as the communication between the messaging stack and
+ * the (D)TLS handshake protocol implementation.
+ *
+ * Readers provide a means of transferring incoming data from
+ * a 'producer' providing it in chunks of arbitrary size, to
+ * a 'consumer' which fetches and processes it in chunks of
+ * again arbitrary, and potentially different, size.
+ *
+ * Readers can thus be seen as datagram-to-stream converters,
+ * and they abstract away the following two tasks from the user:
+ * 1. The pointer arithmetic of stepping through a producer-
+ * provided chunk in smaller chunks.
+ * 2. The merging of incoming data chunks in case the
+ * consumer requests data in larger chunks than what the
+ * producer provides.
+ *
+ * The basic abstract flow of operation is the following:
+ * - Initially, the reader is in 'producing mode'.
+ * - The producer hands an incoming data buffer to the reader,
+ * moving it from 'producing' to 'consuming' mode.
+ * - The consumer subsequently fetches and processes the buffer
+ * content. Once that's done -- or partially done and a consumer's
+ * request can't be fulfilled -- the producer revokes the reader's
+ * access to the incoming data buffer, putting the reader back to
+ * producing mode.
+ * - The producer subsequently gathers more incoming data and hands
+ * it to the reader until it switches back to consuming mode
+ * if enough data is available for the last consumer request to
+ * be satisfiable.
+ * - Repeat the above.
+ *
+ * The abstract states of the reader from the producer's and
+ * consumer's perspective are as follows:
+ *
+ * - From the perspective of the consumer, the state of the
+ * reader consists of the following:
+ * - A byte stream representing (concatenation of) the data
+ * received through calls to mbedtls_mps_reader_get(),
+ * - A marker within that byte stream indicating which data
+ * can be considered processed, and hence need not be retained,
+ * when the reader is passed back to the producer via
+ * mbedtls_mps_reader_reclaim().
+ * The marker is set via mbedtls_mps_reader_commit()
+ * which places it at the end of the current byte stream.
+ * The consumer need not be aware of the distinction between consumer
+ * and producer mode, because it only interfaces with the reader
+ * when the latter is in consuming mode.
+ *
+ * - From the perspective of the producer, the reader's state is one of:
+ * - Attached: The reader is in consuming mode.
+ * - Unset: No incoming data buffer is currently managed by the reader,
+ * and all previously handed incoming data buffers have been
+ * fully processed. More data needs to be fed into the reader
+ * via mbedtls_mps_reader_feed().
+ *
+ * - Accumulating: No incoming data buffer is currently managed by the
+ * reader, but some data from the previous incoming data
+ * buffer hasn't been processed yet and is internally
+ * held back.
+ * The Attached state belongs to consuming mode, while the Unset and
+ * Accumulating states belong to producing mode.
+ *
+ * Transitioning from the Unset or Accumulating state to Attached is
+ * done via successful calls to mbedtls_mps_reader_feed(), while
+ * transitioning from Attached to either Unset or Accumulating (depending
+ * on what has been processed) is done via mbedtls_mps_reader_reclaim().
+ *
+ * The following diagram depicts the producer-state progression:
+ *
+ * +------------------+ reclaim
+ * | Unset +<-------------------------------------+ get
+ * +--------|---------+ | +------+
+ * | | | |
+ * | | | |
+ * | feed +---------+---+--+ |
+ * +--------------------------------------> <---+
+ * | Attached |
+ * +--------------------------------------> <---+
+ * | feed, enough data available +---------+---+--+ |
+ * | to serve previous consumer request | | |
+ * | | | |
+ * +--------+---------+ | +------+
+ * +----> Accumulating |<-------------------------------------+ commit
+ * | +---+--------------+ reclaim, previous read request
+ * | | couldn't be fulfilled
+ * | |
+ * +--------+
+ * feed, need more data to serve
+ * previous consumer request
+ * |
+ * |
+ * producing mode | consuming mode
+ * |
+ *
+ */
+
+#ifndef MBEDTLS_READER_H
+#define MBEDTLS_READER_H
+
+#include <stdio.h>
+
+#include "mps_common.h"
+#include "mps_error.h"
+
+struct mbedtls_mps_reader;
+typedef struct mbedtls_mps_reader mbedtls_mps_reader;
+
+/*
+ * Structure definitions
+ */
+
+struct mbedtls_mps_reader
+{
+ unsigned char *frag; /*!< The fragment of incoming data managed by
+ * the reader; it is provided to the reader
+ * through mbedtls_mps_reader_feed(). The reader
+ * does not own the fragment and does not
+ * perform any allocation operations on it,
+ * but does have read and write access to it.
+ *
+ * The reader is in consuming mode if
+ * and only if \c frag is not \c NULL. */
+ mbedtls_mps_stored_size_t frag_len;
+ /*!< The length of the current fragment.
+ * Must be 0 if \c frag == \c NULL. */
+ mbedtls_mps_stored_size_t commit;
+ /*!< The offset of the last commit, relative
+ * to the first byte in the fragment, if
+ * no accumulator is present. If an accumulator
+ * is present, it is viewed as a prefix to the
+ * current fragment, and this variable contains
+ * an offset from the beginning of the accumulator.
+ *
+ * This is only used when the reader is in
+ * consuming mode, i.e. \c frag != \c NULL;
+ * otherwise, its value is \c 0. */
+ mbedtls_mps_stored_size_t end;
+ /*!< The offset of the end of the last chunk
+ * passed to the user through a call to
+ * mbedtls_mps_reader_get(), relative to the first
+ * byte in the fragment, if no accumulator is
+ * present. If an accumulator is present, it is
+ * viewed as a prefix to the current fragment, and
+ * this variable contains an offset from the
+ * beginning of the accumulator.
+ *
+ * This is only used when the reader is in
+ * consuming mode, i.e. \c frag != \c NULL;
+ * otherwise, its value is \c 0. */
+ mbedtls_mps_stored_size_t pending;
+ /*!< The amount of incoming data missing on the
+ * last call to mbedtls_mps_reader_get().
+ * In particular, it is \c 0 if the last call
+ * was successful.
+ * If a reader is reclaimed after an
+ * unsuccessful call to mbedtls_mps_reader_get(),
+ * this variable is used to have the reader
+ * remember how much data should be accumulated
+ * so that the call to mbedtls_mps_reader_get()
+ * succeeds next time.
+ * This is only used when the reader is in
+ * consuming mode, i.e. \c frag != \c NULL;
+ * otherwise, its value is \c 0. */
+
+ /* The accumulator is only needed if we need to be able to pause
+ * the reader. A few bytes could be saved by moving this to a
+ * separate struct and using a pointer here. */
+
+ unsigned char *acc; /*!< The accumulator is used to gather incoming
+ * data if a read-request via mbedtls_mps_reader_get()
+ * cannot be served from the current fragment. */
+ mbedtls_mps_stored_size_t acc_len;
+ /*!< The total size of the accumulator. */
+ mbedtls_mps_stored_size_t acc_available;
+ /*!< The number of bytes currently gathered in
+ * the accumulator. This is both used in
+ * producing and in consuming mode:
+ * While producing, it is increased until
+ * it reaches the value of \c acc_remaining below.
+ * While consuming, it is used to judge if a
+ * get request can be served from the
+ * accumulator or not.
+ * Must not be larger than \c acc_len. */
+ union
+ {
+ mbedtls_mps_stored_size_t acc_remaining;
+ /*!< This indicates the amount of data still
+ * to be gathered in the accumulator. It is
+ * only used in producing mode.
+ * Must be at most acc_len - acc_available. */
+ mbedtls_mps_stored_size_t frag_offset;
+ /*!< If an accumulator is present and in use, this
+ * field indicates the offset of the current
+ * fragment from the beginning of the
+ * accumulator. If no accumulator is present
+ * or the accumulator is not in use, this is \c 0.
+ * It is only used in consuming mode.
+ * Must not be larger than \c acc_available. */
+ } acc_share;
+};
+
+/*
+ * API organization:
+ * A reader object is usually prepared and maintained
+ * by some lower layer and passed for usage to an upper
+ * layer, and the API naturally splits according to which
+ * layer is supposed to use the respective functions.
+ */
+
+/*
+ * Maintenance API (Lower layer)
+ */
+
+/**
+ * \brief Initialize a reader object
+ *
+ * \param reader The reader to be initialized.
+ * \param acc The buffer to be used as a temporary accumulator
+ * in case get requests through mbedtls_mps_reader_get()
+ * exceed the buffer provided by mbedtls_mps_reader_feed().
+ * This buffer is owned by the caller and exclusive use
+ * for reading and writing is given to the reader for the
+ * duration of the reader's lifetime. It is thus the caller's
+ * responsibility to maintain (and not touch) the buffer for
+ * the lifetime of the reader, and to properly zeroize and
+ * free the memory after the reader has been destroyed.
+ * \param acc_len The size in Bytes of \p acc.
+ *
+ * \return \c 0 on success.
+ * \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure.
+ */
+int mbedtls_mps_reader_init( mbedtls_mps_reader *reader,
+ unsigned char *acc,
+ mbedtls_mps_size_t acc_len );
+
+/**
+ * \brief Free a reader object
+ *
+ * \param reader The reader to be freed.
+ *
+ * \return \c 0 on success.
+ * \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure.
+ */
+int mbedtls_mps_reader_free( mbedtls_mps_reader *reader );
+
+/**
+ * \brief Pass chunk of data for the reader to manage.
+ *
+ * \param reader The reader context to use. The reader must be
+ * in producing mode.
+ * \param buf The buffer to be managed by the reader.
+ * \param buflen The size in Bytes of \p buffer.
+ *
+ * \return \c 0 on success. In this case, the reader will be
+ * moved to consuming mode and obtains read access
+ * of \p buf until mbedtls_mps_reader_reclaim()
+ * is called. It is the responsibility of the caller
+ * to ensure that the \p buf persists and is not changed
+ * between successful calls to mbedtls_mps_reader_feed()
+ * and mbedtls_mps_reader_reclaim().
+ * \return \c MBEDTLS_ERR_MPS_READER_NEED_MORE if more input data is
+ * required to fulfill a previous request to mbedtls_mps_reader_get().
+ * In this case, the reader remains in producing mode and
+ * takes no ownership of the provided buffer (an internal copy
+ * is made instead).
+ * \return Another negative \c MBEDTLS_ERR_READER_XXX error code on
+ * different kinds of failures.
+ */
+int mbedtls_mps_reader_feed( mbedtls_mps_reader *reader,
+ unsigned char *buf,
+ mbedtls_mps_size_t buflen );
+
+/**
+ * \brief Reclaim reader's access to the current input buffer.
+ *
+ * \param reader The reader context to use. The reader must be
+ * in consuming mode.
+ * \param paused If not \c NULL, the integer at address \p paused will be
+ * modified to indicate whether the reader has been paused
+ * (value \c 1) or not (value \c 0). Pausing happens if there
+ * is uncommitted data and a previous request to
+ * mbedtls_mps_reader_get() has exceeded the bounds of the
+ * input buffer.
+ *
+ * \return \c 0 on success.
+ * \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure.
+ */
+int mbedtls_mps_reader_reclaim( mbedtls_mps_reader *reader,
+ int *paused );
+
+/*
+ * Usage API (Upper layer)
+ */
+
+/**
+ * \brief Request data from the reader.
+ *
+ * \param reader The reader context to use. The reader must
+ * be in consuming mode.
+ * \param desired The desired amount of data to be read, in Bytes.
+ * \param buffer The address to store the buffer pointer in.
+ * This must not be \c NULL.
+ * \param buflen The address to store the actual buffer
+ * length in, or \c NULL.
+ *
+ * \return \c 0 on success. In this case, \c *buf holds the
+ * address of a buffer of size \c *buflen
+ * (if \c buflen != \c NULL) or \c desired
+ * (if \c buflen == \c NULL). The user has read access
+ * to the buffer and guarantee of stability of the data
+ * until the next call to mbedtls_mps_reader_reclaim().
+ * \return #MBEDTLS_ERR_MPS_READER_OUT_OF_DATA if there is not enough
+ * data available to serve the get request. In this case, the
+ * reader remains intact and in consuming mode, and the consumer
+ * should retry the call after a successful cycle of
+ * mbedtls_mps_reader_reclaim() and mbedtls_mps_reader_feed().
+ * If, after such a cycle, the consumer requests a different
+ * amount of data, the result is implementation-defined;
+ * progress is guaranteed only if the same amount of data
+ * is requested after a mbedtls_mps_reader_reclaim() and
+ * mbedtls_mps_reader_feed() cycle.
+ * \return Another negative \c MBEDTLS_ERR_READER_XXX error
+ * code for different kinds of failure.
+ *
+ * \note Passing \c NULL as \p buflen is a convenient way to
+ * indicate that fragmentation is not tolerated.
+ * It's functionally equivalent to passing a valid
+ * address as buflen and checking \c *buflen == \c desired
+ * afterwards.
+ */
+int mbedtls_mps_reader_get( mbedtls_mps_reader *reader,
+ mbedtls_mps_size_t desired,
+ unsigned char **buffer,
+ mbedtls_mps_size_t *buflen );
+
+/**
+ * \brief Mark data obtained from mbedtls_mps_reader_get() as processed.
+ *
+ * This call indicates that all data received from prior calls to
+ * mbedtls_mps_reader_get() has been or will have been
+ * processed when mbedtls_mps_reader_reclaim() is called,
+ * and thus need not be backed up.
+ *
+ * This function has no user observable effect until
+ * mbedtls_mps_reader_reclaim() is called. In particular,
+ * buffers received from mbedtls_mps_reader_get() remain
+ * valid until mbedtls_mps_reader_reclaim() is called.
+ *
+ * \param reader The reader context to use.
+ *
+ * \return \c 0 on success.
+ * \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure.
+ *
+ */
+int mbedtls_mps_reader_commit( mbedtls_mps_reader *reader );
+
+#endif /* MBEDTLS_READER_H */
diff --git a/library/mps_trace.c b/library/mps_trace.c
new file mode 100644
index 0000000..dc0577d
--- /dev/null
+++ b/library/mps_trace.c
@@ -0,0 +1,127 @@
+/*
+ * Message Processing Stack, Trace module
+ *
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of Mbed TLS (https://tls.mbed.org)
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL)
+
+#include "mps_common.h"
+
+#if defined(MBEDTLS_MPS_ENABLE_TRACE)
+
+#include "mps_trace.h"
+#include <stdarg.h>
+
+static int trace_depth = 0;
+
+#define color_default "\x1B[0m"
+#define color_red "\x1B[1;31m"
+#define color_green "\x1B[1;32m"
+#define color_yellow "\x1B[1;33m"
+#define color_blue "\x1B[1;34m"
+#define color_magenta "\x1B[1;35m"
+#define color_cyan "\x1B[1;36m"
+#define color_white "\x1B[1;37m"
+
+static char const * colors[] =
+{
+ color_default,
+ color_green,
+ color_yellow,
+ color_magenta,
+ color_cyan,
+ color_blue,
+ color_white
+};
+
+#define MPS_TRACE_BUF_SIZE 100
+
+void mbedtls_mps_trace_print_msg( int id, int line, const char *format, ... )
+{
+ int ret;
+ char str[MPS_TRACE_BUF_SIZE];
+ va_list argp;
+ va_start( argp, format );
+ ret = mbedtls_vsnprintf( str, MPS_TRACE_BUF_SIZE, format, argp );
+ va_end( argp );
+
+ if( ret >= 0 && ret < MPS_TRACE_BUF_SIZE )
+ {
+ str[ret] = '\0';
+ mbedtls_printf( "[%d|L%d]: %s\n", id, line, str );
+ }
+}
+
+int mbedtls_mps_trace_get_depth()
+{
+ return trace_depth;
+}
+void mbedtls_mps_trace_dec_depth()
+{
+ trace_depth--;
+}
+void mbedtls_mps_trace_inc_depth()
+{
+ trace_depth++;
+}
+
+void mbedtls_mps_trace_color( int id )
+{
+ if( id > (int) ( sizeof( colors ) / sizeof( *colors ) ) )
+ return;
+ printf( "%s", colors[ id ] );
+}
+
+void mbedtls_mps_trace_indent( int level, mbedtls_mps_trace_type ty )
+{
+ if( level > 0 )
+ {
+ while( --level )
+ printf( "| " );
+
+ printf( "| " );
+ }
+
+ switch( ty )
+ {
+ case mbedtls_mps_trace_comment:
+ mbedtls_printf( "@ " );
+ break;
+
+ case mbedtls_mps_trace_call:
+ mbedtls_printf( "+--> " );
+ break;
+
+ case mbedtls_mps_trace_error:
+ mbedtls_printf( "E " );
+ break;
+
+ case mbedtls_mps_trace_return:
+ mbedtls_printf( "< " );
+ break;
+
+ default:
+ break;
+ }
+}
+
+#endif /* MBEDTLS_MPS_ENABLE_TRACE */
+#endif /* MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL */
diff --git a/library/mps_trace.h b/library/mps_trace.h
new file mode 100644
index 0000000..048d573
--- /dev/null
+++ b/library/mps_trace.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ */
+
+/**
+ * \file mps_trace.h
+ *
+ * \brief Tracing module for MPS
+ */
+
+#ifndef MBEDTLS_MPS_MBEDTLS_MPS_TRACE_H
+#define MBEDTLS_MPS_MBEDTLS_MPS_TRACE_H
+
+#include "common.h"
+#include "mps_common.h"
+#include "mps_trace.h"
+
+#if defined(MBEDTLS_PLATFORM_C)
+#include "mbedtls/platform.h"
+#else
+#include <stdio.h>
+#define mbedtls_printf printf
+#define mbedtls_vsnprintf vsnprintf
+#endif /* MBEDTLS_PLATFORM_C */
+
+#if defined(MBEDTLS_MPS_ENABLE_TRACE)
+
+/*
+ * Adapt this to enable/disable tracing output
+ * from the various layers of the MPS.
+ */
+
+#define MBEDTLS_MPS_TRACE_ENABLE_LAYER_1
+#define MBEDTLS_MPS_TRACE_ENABLE_LAYER_2
+#define MBEDTLS_MPS_TRACE_ENABLE_LAYER_3
+#define MBEDTLS_MPS_TRACE_ENABLE_LAYER_4
+#define MBEDTLS_MPS_TRACE_ENABLE_READER
+#define MBEDTLS_MPS_TRACE_ENABLE_WRITER
+
+/*
+ * To use the existing trace module, only change
+ * MBEDTLS_MPS_TRACE_ENABLE_XXX above, but don't modify the
+ * rest of this file.
+ */
+
+typedef enum
+{
+ mbedtls_mps_trace_comment,
+ mbedtls_mps_trace_call,
+ mbedtls_mps_trace_error,
+ mbedtls_mps_trace_return
+} mbedtls_mps_trace_type;
+
+#define MBEDTLS_MPS_TRACE_BIT_LAYER_1 1
+#define MBEDTLS_MPS_TRACE_BIT_LAYER_2 2
+#define MBEDTLS_MPS_TRACE_BIT_LAYER_3 3
+#define MBEDTLS_MPS_TRACE_BIT_LAYER_4 4
+#define MBEDTLS_MPS_TRACE_BIT_WRITER 5
+#define MBEDTLS_MPS_TRACE_BIT_READER 6
+
+#if defined(MBEDTLS_MPS_TRACE_ENABLE_LAYER_1)
+#define MBEDTLS_MPS_TRACE_MASK_LAYER_1 (1u << MBEDTLS_MPS_TRACE_BIT_LAYER_1 )
+#else
+#define MBEDTLS_MPS_TRACE_MASK_LAYER_1 0
+#endif
+
+#if defined(MBEDTLS_MPS_TRACE_ENABLE_LAYER_2)
+#define MBEDTLS_MPS_TRACE_MASK_LAYER_2 (1u << MBEDTLS_MPS_TRACE_BIT_LAYER_2 )
+#else
+#define MBEDTLS_MPS_TRACE_MASK_LAYER_2 0
+#endif
+
+#if defined(MBEDTLS_MPS_TRACE_ENABLE_LAYER_3)
+#define MBEDTLS_MPS_TRACE_MASK_LAYER_3 (1u << MBEDTLS_MPS_TRACE_BIT_LAYER_3 )
+#else
+#define MBEDTLS_MPS_TRACE_MASK_LAYER_3 0
+#endif
+
+#if defined(MBEDTLS_MPS_TRACE_ENABLE_LAYER_4)
+#define MBEDTLS_MPS_TRACE_MASK_LAYER_4 (1u << MBEDTLS_MPS_TRACE_BIT_LAYER_4 )
+#else
+#define MBEDTLS_MPS_TRACE_MASK_LAYER_4 0
+#endif
+
+#if defined(MBEDTLS_MPS_TRACE_ENABLE_READER)
+#define MBEDTLS_MPS_TRACE_MASK_READER (1u << MBEDTLS_MPS_TRACE_BIT_READER )
+#else
+#define MBEDTLS_MPS_TRACE_MASK_READER 0
+#endif
+
+#if defined(MBEDTLS_MPS_TRACE_ENABLE_WRITER)
+#define MBEDTLS_MPS_TRACE_MASK_WRITER (1u << MBEDTLS_MPS_TRACE_BIT_WRITER )
+#else
+#define MBEDTLS_MPS_TRACE_MASK_WRITER 0
+#endif
+
+#define MBEDTLS_MPS_TRACE_MASK ( MBEDTLS_MPS_TRACE_MASK_LAYER_1 | \
+ MBEDTLS_MPS_TRACE_MASK_LAYER_2 | \
+ MBEDTLS_MPS_TRACE_MASK_LAYER_3 | \
+ MBEDTLS_MPS_TRACE_MASK_LAYER_4 | \
+ MBEDTLS_MPS_TRACE_MASK_READER | \
+ MBEDTLS_MPS_TRACE_MASK_WRITER )
+
+/* We have to avoid globals because E-ACSL chokes on them...
+ * Wrap everything in stub functions. */
+int mbedtls_mps_trace_get_depth( void );
+void mbedtls_mps_trace_inc_depth( void );
+void mbedtls_mps_trace_dec_depth( void );
+
+void mbedtls_mps_trace_color( int id );
+void mbedtls_mps_trace_indent( int level, mbedtls_mps_trace_type ty );
+
+void mbedtls_mps_trace_print_msg( int id, int line, const char *format, ... );
+
+#define MBEDTLS_MPS_TRACE( type, ... ) \
+ do { \
+ if( ! ( MBEDTLS_MPS_TRACE_MASK & ( 1u << mbedtls_mps_trace_id ) ) ) \
+ break; \
+ mbedtls_mps_trace_indent( mbedtls_mps_trace_get_depth(), type ); \
+ mbedtls_mps_trace_color( mbedtls_mps_trace_id ); \
+ mbedtls_mps_trace_print_msg( mbedtls_mps_trace_id, __LINE__, __VA_ARGS__ ); \
+ mbedtls_mps_trace_color( 0 ); \
+ } while( 0 )
+
+#define MBEDTLS_MPS_TRACE_INIT( ... ) \
+ do { \
+ if( ! ( MBEDTLS_MPS_TRACE_MASK & ( 1u << mbedtls_mps_trace_id ) ) ) \
+ break; \
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_call, __VA_ARGS__ ); \
+ mbedtls_mps_trace_inc_depth(); \
+ } while( 0 )
+
+#define MBEDTLS_MPS_TRACE_END( val ) \
+ do { \
+ if( ! ( MBEDTLS_MPS_TRACE_MASK & ( 1u << mbedtls_mps_trace_id ) ) ) \
+ break; \
+ MBEDTLS_MPS_TRACE( mbedtls_mps_trace_return, "%d (-%#04x)", \
+ (int) (val), -((unsigned)(val)) ); \
+ mbedtls_mps_trace_dec_depth(); \
+ } while( 0 )
+
+#define MBEDTLS_MPS_TRACE_RETURN( val ) \
+ do { \
+ /* Breaks tail recursion. */ \
+ int ret__ = val; \
+ MBEDTLS_MPS_TRACE_END( ret__ ); \
+ return( ret__ ); \
+ } while( 0 )
+
+#else /* MBEDTLS_MPS_TRACE */
+
+#define MBEDTLS_MPS_TRACE( type, ... ) do { } while( 0 )
+#define MBEDTLS_MPS_TRACE_INIT( ... ) do { } while( 0 )
+#define MBEDTLS_MPS_TRACE_END do { } while( 0 )
+
+#define MBEDTLS_MPS_TRACE_RETURN( val ) return( val );
+
+#endif /* MBEDTLS_MPS_TRACE */
+
+#endif /* MBEDTLS_MPS_MBEDTLS_MPS_TRACE_H */
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index c141704..049b130 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -134,6 +134,7 @@
add_test_suite(mdx)
add_test_suite(memory_buffer_alloc)
add_test_suite(mpi)
+add_test_suite(mps)
add_test_suite(net)
add_test_suite(nist_kw)
add_test_suite(oid)
diff --git a/tests/suites/test_suite_mps.data b/tests/suites/test_suite_mps.data
new file mode 100644
index 0000000..442f321
--- /dev/null
+++ b/tests/suites/test_suite_mps.data
@@ -0,0 +1,125 @@
+MPS Reader: Single step, single round, pausing disabled
+mbedtls_mps_reader_no_pausing_single_step_single_round:0
+
+MPS Reader: Single step, single round, pausing enabled but unused
+mbedtls_mps_reader_no_pausing_single_step_single_round:1
+
+MPS Reader: Single step, multiple rounds, pausing disabled
+mbedtls_mps_reader_no_pausing_single_step_multiple_rounds:0
+
+MPS Reader: Single step, multiple rounds, pausing enabled but unused
+mbedtls_mps_reader_no_pausing_single_step_multiple_rounds:1
+
+MPS Reader: Multiple steps, single round, pausing disabled
+mbedtls_mps_reader_no_pausing_multiple_steps_single_round:0
+
+MPS Reader: Multiple steps, single round, pausing enabled but unused
+mbedtls_mps_reader_no_pausing_multiple_steps_single_round:1
+
+MPS Reader: Multiple steps, multiple rounds, pausing disabled
+mbedtls_mps_reader_no_pausing_multiple_steps_multiple_rounds:0
+
+MPS Reader: Multiple steps, multiple rounds, pausing enabled but unused
+mbedtls_mps_reader_no_pausing_multiple_steps_multiple_rounds:1
+
+MPS Reader: Pausing needed but disabled
+mbedtls_mps_reader_pausing_needed_disabled:
+
+MPS Reader: Pausing needed + enabled, but buffer too small
+mbedtls_mps_reader_pausing_needed_buffer_too_small:
+
+MPS Reader: Pausing, repeat single call without commit
+mbedtls_mps_reader_pausing:0
+
+MPS Reader: Pausing, repeat single call with commit
+mbedtls_mps_reader_pausing:1
+
+MPS Reader: Pausing, repeat multiple calls without commit
+mbedtls_mps_reader_pausing:2
+
+MPS Reader: Pausing, repeat multiple calls with commit #0
+mbedtls_mps_reader_pausing:3
+
+MPS Reader: Pausing, repeat multiple calls with commit #1
+mbedtls_mps_reader_pausing:4
+
+MPS Reader: Pausing, repeat multiple calls with commit #2
+mbedtls_mps_reader_pausing:5
+
+MPS Reader: Pausing, feed 50 bytes in 10b + 10b + 80b
+mbedtls_mps_reader_pausing_multiple_feeds:0
+
+MPS Reader: Pausing, feed 50 bytes in 50x1b
+mbedtls_mps_reader_pausing_multiple_feeds:1
+
+MPS Reader: Pausing, feed 50 bytes in 49x1b + 51b
+mbedtls_mps_reader_pausing_multiple_feeds:2
+
+MPS Reader: Reclaim with data remaining #0
+mbedtls_mps_reader_reclaim_data_left:0
+
+MPS Reader: Reclaim with data remaining #1
+mbedtls_mps_reader_reclaim_data_left:1
+
+MPS Reader: Reclaim with data remaining #2
+mbedtls_mps_reader_reclaim_data_left:2
+
+MPS Reader: Reclaim with data remaining, continue fetching
+mbedtls_mps_reader_reclaim_data_left_retry:
+
+MPS Reader: Pausing several times, #0
+mbedtls_mps_reader_multiple_pausing:0
+
+MPS Reader: Pausing several times, #1
+mbedtls_mps_reader_multiple_pausing:1
+
+MPS Reader: Pausing several times, #2
+mbedtls_mps_reader_multiple_pausing:2
+
+MPS Reader: Pausing several times, #3
+mbedtls_mps_reader_multiple_pausing:3
+
+MPS Reader: Random usage, 20 rds, feed 100, get 200, acc 50
+mbedtls_mps_reader_random_usage:20:100:200:50
+
+MPS Reader: Random usage, 1000 rds, feed 10, get 100, acc 80
+mbedtls_mps_reader_random_usage:1000:10:100:80
+
+MPS Reader: Random usage, 10000 rds, feed 1, get 100, acc 80
+mbedtls_mps_reader_random_usage:10000:1:100:80
+
+MPS Reader: Random usage, 100 rds, feed 100, get 1000, acc 500
+mbedtls_mps_reader_random_usage:100:100:1000:500
+
+MPS Reader: Pausing, inconsistent continuation, #0
+mbedtls_reader_inconsistent_usage:0
+
+MPS Reader: Pausing, inconsistent continuation, #1
+mbedtls_reader_inconsistent_usage:1
+
+MPS Reader: Pausing, inconsistent continuation, #2
+mbedtls_reader_inconsistent_usage:2
+
+MPS Reader: Pausing, inconsistent continuation, #3
+mbedtls_reader_inconsistent_usage:3
+
+MPS Reader: Pausing, inconsistent continuation, #4
+mbedtls_reader_inconsistent_usage:4
+
+MPS Reader: Pausing, inconsistent continuation, #5
+mbedtls_reader_inconsistent_usage:5
+
+MPS Reader: Pausing, inconsistent continuation, #6
+mbedtls_reader_inconsistent_usage:6
+
+MPS Reader: Pausing, inconsistent continuation, #7
+mbedtls_reader_inconsistent_usage:7
+
+MPS Reader: Pausing, inconsistent continuation, #8
+mbedtls_reader_inconsistent_usage:8
+
+MPS Reader: Feed with invalid buffer (NULL)
+mbedtls_mps_reader_feed_empty:
+
+MPS Reader: Excess request leading to integer overflow
+mbedtls_mps_reader_reclaim_overflow:
diff --git a/tests/suites/test_suite_mps.function b/tests/suites/test_suite_mps.function
new file mode 100644
index 0000000..9df8a3c
--- /dev/null
+++ b/tests/suites/test_suite_mps.function
@@ -0,0 +1,1148 @@
+/* BEGIN_HEADER */
+
+#include <stdlib.h>
+
+#include "mps_reader.h"
+
+/*
+ * Compile-time configuration for test suite.
+ */
+
+/* Comment/Uncomment this to disable/enable the
+ * testing of the various MPS layers.
+ * This can be useful for time-consuming instrumentation
+ * tasks such as the conversion of E-ACSL annotations
+ * into runtime assertions. */
+#define TEST_SUITE_MPS_READER
+
+/* End of compile-time configuration. */
+
+/* END_HEADER */
+
+/* BEGIN_DEPENDENCIES
+ * depends_on:MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL
+ * END_DEPENDENCIES
+ */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_no_pausing_single_step_single_round( int with_acc )
+{
+ /* This test exercises the most basic use of the MPS reader:
+ * - The 'producing' layer provides a buffer
+ * - The 'consuming' layer fetches it in a single go.
+ * - After processing, the consuming layer commits the data
+ * and the reader is moved back to producing mode.
+ *
+ * Parameters:
+ * - with_acc: 0 if the reader should be initialized without accumulator.
+ * 1 if the reader should be initialized with accumulator.
+ *
+ * Whether the accumulator is present or not should not matter,
+ * since the consumer's request can be fulfilled from the data
+ * that the producer has provided.
+ */
+ unsigned char bufA[100];
+ unsigned char acc[10];
+ unsigned char *tmp;
+ int paused;
+ mbedtls_mps_reader rd;
+ for( size_t i=0; (unsigned) i < sizeof( bufA ); i++ )
+ bufA[i] = (unsigned char) i;
+
+ /* Preparation (lower layer) */
+ if( with_acc == 0 )
+ mbedtls_mps_reader_init( &rd, NULL, 0 );
+ else
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufA, sizeof( bufA ) ) == 0 );
+ /* Consumption (upper layer) */
+ /* Consume exactly what's available */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 100, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 100, bufA, 100 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ /* Wrapup (lower layer) */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, &paused ) == 0 );
+ TEST_ASSERT( paused == 0 );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_no_pausing_single_step_multiple_rounds( int with_acc )
+{
+ /* This test exercises multiple rounds of the basic use of the MPS reader:
+ * - The 'producing' layer provides a buffer
+ * - The 'consuming' layer fetches it in a single go.
+ * - After processing, the consuming layer commits the data
+ * and the reader is moved back to producing mode.
+ *
+ * Parameters:
+ * - with_acc: 0 if the reader should be initialized without accumulator.
+ * 1 if the reader should be initialized with accumulator.
+ *
+ * Whether the accumulator is present or not should not matter,
+ * since the consumer's request can be fulfilled from the data
+ * that the producer has provided.
+ */
+
+ unsigned char bufA[100], bufB[100];
+ unsigned char acc[10];
+ unsigned char *tmp;
+ mbedtls_mps_reader rd;
+ for( size_t i=0; (unsigned) i < sizeof( bufA ); i++ )
+ bufA[i] = (unsigned char) i;
+ for( size_t i=0; (unsigned) i < sizeof( bufB ); i++ )
+ bufB[i] = ~ ((unsigned char) i);
+
+ /* Preparation (lower layer) */
+ if( with_acc == 0 )
+ mbedtls_mps_reader_init( &rd, NULL, 0 );
+ else
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufA, sizeof( bufA ) ) == 0 );
+ /* Consumption (upper layer) */
+ /* Consume exactly what's available */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 100, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 100, bufA, 100 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ /* Preparation */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB, sizeof( bufB ) ) == 0 );
+ /* Consumption */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 100, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 100, bufB, 100 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ /* Wrapup (lower layer) */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_no_pausing_multiple_steps_single_round( int with_acc )
+{
+ /* This test exercises one round of the following:
+ * - The 'producing' layer provides a buffer
+ * - The 'consuming' layer fetches it in multiple calls
+ * to `mbedtls_mps_reader_get()`, without committing in between.
+ * - After processing, the consuming layer commits the data
+ * and the reader is moved back to producing mode.
+ *
+ * Parameters:
+ * - with_acc: 0 if the reader should be initialized without accumulator.
+ * 1 if the reader should be initialized with accumulator.
+ *
+ * Whether the accumulator is present or not should not matter,
+ * since the consumer's requests can be fulfilled from the data
+ * that the producer has provided.
+ */
+
+ /* Lower layer provides data that the upper layer fully consumes
+ * through multiple `get` calls. */
+ unsigned char buf[100];
+ unsigned char acc[10];
+ unsigned char *tmp;
+ mbedtls_mps_size_t tmp_len;
+ mbedtls_mps_reader rd;
+ for( size_t i=0; (unsigned) i < sizeof( buf ); i++ )
+ buf[i] = (unsigned char) i;
+
+ /* Preparation (lower layer) */
+ if( with_acc == 0 )
+ mbedtls_mps_reader_init( &rd, NULL, 0 );
+ else
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, buf, sizeof( buf ) ) == 0 );
+ /* Consumption (upper layer) */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, buf, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 70, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 70, buf + 10, 70 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 30, &tmp, &tmp_len ) == 0 );
+ ASSERT_COMPARE( tmp, tmp_len, buf + 80, 20 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ /* Wrapup (lower layer) */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_no_pausing_multiple_steps_multiple_rounds( int with_acc )
+{
+ /* This test exercises one round of fetching a buffer in multiple chunks
+ * and passing it back to the producer afterwards, followed by another
+ * single-step sequence of feed-fetch-commit-reclaim.
+ */
+ unsigned char bufA[100], bufB[100];
+ unsigned char acc[10];
+ unsigned char *tmp;
+ mbedtls_mps_size_t tmp_len;
+ mbedtls_mps_reader rd;
+ for( size_t i=0; (unsigned) i < sizeof( bufA ); i++ )
+ bufA[i] = (unsigned char) i;
+ for( size_t i=0; (unsigned) i < sizeof( bufB ); i++ )
+ bufB[i] = ~ ((unsigned char) i);
+
+ /* Preparation (lower layer) */
+ if( with_acc == 0 )
+ mbedtls_mps_reader_init( &rd, NULL, 0 );
+ else
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufA, sizeof( bufA ) ) == 0 );
+ /* Consumption (upper layer) */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 70, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 70, bufA + 10, 70 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 30, &tmp, &tmp_len ) == 0 );
+ ASSERT_COMPARE( tmp, tmp_len, bufA + 80, 20 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ /* Preparation */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB, sizeof( bufB ) ) == 0 );
+ /* Consumption */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 100, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 100, bufB, 100 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ /* Wrapup */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_pausing_needed_disabled()
+{
+ /* This test exercises the behaviour of the MPS reader when a read request
+ * of the consumer exceeds what has been provided by the producer, and when
+ * no accumulator is available in the reader.
+ *
+ * In this case, we expect the reader to fail.
+ */
+
+ unsigned char buf[100];
+ unsigned char *tmp;
+ mbedtls_mps_reader rd;
+ for( size_t i=0; (unsigned) i < sizeof( buf ); i++ )
+ buf[i] = (unsigned char) i;
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, NULL, 0 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, buf, sizeof( buf ) ) == 0 );
+ /* Consumption (upper layer) */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 50, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 50, buf, 50 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 100, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+ /* Wrapup (lower layer) */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_NEED_ACCUMULATOR );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_pausing_needed_buffer_too_small()
+{
+ /* This test exercises the behaviour of the MPS reader with accumulator
+ * in the situation where a read request goes beyond the bounds of the
+ * current read buffer, _and_ the reader's accumulator is too small to
+ * hold the requested amount of data.
+ *
+ * In this case, we expect mbedtls_mps_reader_reclaim() to fail,
+ * but it should be possible to continue fetching data as if
+ * there had been no excess request via mbedtls_mps_reader_get()
+ * and the call to mbedtls_mps_reader_reclaim() had been rejected
+ * because of data remaining.
+ */
+
+ unsigned char buf[100];
+ unsigned char acc[10];
+ unsigned char *tmp;
+ mbedtls_mps_reader rd;
+ mbedtls_mps_size_t tmp_len;
+
+ for( size_t i=0; (unsigned) i < sizeof( buf ); i++ )
+ buf[i] = (unsigned char) i;
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, buf, sizeof( buf ) ) == 0 );
+ /* Consumption (upper layer) */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 50, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 50, buf, 50 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, buf + 50, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 100, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+ /* Wrapup (lower layer) */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
+
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 50, &tmp, &tmp_len ) == 0 );
+ ASSERT_COMPARE( tmp, tmp_len, buf + 50, 50 );
+
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_reclaim_overflow()
+{
+ /* This test exercises the behaviour of the MPS reader with accumulator
+ * in the situation where upon calling mbedtls_mps_reader_reclaim(), the
+ * uncommitted data together with the excess data missing in the last
+ * call to medtls_mps_reader_get() exceeds the bounds of the type
+ * holding the buffer length.
+ */
+
+ unsigned char buf[100];
+ unsigned char acc[50];
+ unsigned char *tmp;
+ mbedtls_mps_reader rd;
+
+ for( size_t i=0; (unsigned) i < sizeof( buf ); i++ )
+ buf[i] = (unsigned char) i;
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, buf, sizeof( buf ) ) == 0 );
+ /* Consumption (upper layer) */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 50, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 50, buf, 50 );
+ /* Excess request */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, (mbedtls_mps_size_t) -1, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+ /* Wrapup (lower layer) */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
+
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_pausing( int option )
+{
+ /* This test exercises the behaviour of the reader when the
+ * accumulator is used to fulfill a consumer's request.
+ *
+ * More detailed:
+ * - The producer feeds some data.
+ * - The consumer asks for more data than what's available.
+ * - The reader remembers the request and goes back to
+ * producing mode, waiting for more data from the producer.
+ * - The producer provides another chunk of data which is
+ * sufficient to fulfill the original read request.
+ * - The consumer retries the original read request, which
+ * should now succeed.
+ *
+ * This test comes in multiple variants controlled by the
+ * `option` parameter and documented below.
+ */
+
+ unsigned char bufA[100], bufB[100];
+ unsigned char *tmp;
+ unsigned char acc[40];
+ int paused;
+ mbedtls_mps_reader rd;
+ for( size_t i=0; (unsigned) i < sizeof( bufA ); i++ )
+ bufA[i] = (unsigned char) i;
+ for( size_t i=0; (unsigned) i < sizeof( bufB ); i++ )
+ bufB[i] = ~ ((unsigned char) i);
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufA, sizeof( bufA ) ) == 0 );
+
+ /* Consumption (upper layer) */
+ /* Ask for more than what's available. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 80, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 80, bufA, 80 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 80, 10 );
+ switch( option )
+ {
+ case 0: /* Single uncommitted fetch at pausing */
+ case 1:
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ break;
+ default: /* Multiple uncommitted fetches at pausing */
+ break;
+ }
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+
+ /* Preparation */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, &paused ) == 0 );
+ TEST_ASSERT( paused == 1 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB, sizeof( bufB ) ) == 0 );
+
+ /* Consumption */
+ switch( option )
+ {
+ case 0: /* Single fetch at pausing, re-fetch with commit. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ break;
+
+ case 1: /* Single fetch at pausing, re-fetch without commit. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ break;
+
+ case 2: /* Multiple fetches at pausing, repeat without commit. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 80, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ break;
+
+ case 3: /* Multiple fetches at pausing, repeat with commit 1. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 80, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ break;
+
+ case 4: /* Multiple fetches at pausing, repeat with commit 2. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 80, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ break;
+
+ case 5: /* Multiple fetches at pausing, repeat with commit 3. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 80, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ break;
+
+ default:
+ TEST_ASSERT( 0 );
+ }
+
+ /* In all cases, fetch the rest of the second buffer. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 90, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 90, bufB + 10, 90 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+
+ /* Wrapup */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_pausing_multiple_feeds( int option )
+{
+ /* This test exercises the behaviour of the MPS reader
+ * in the following situation:
+ * - The consumer has asked for more than what's available, so the
+ * reader pauses and waits for further input data via
+ * `mbedtls_mps_reader_feed()`
+ * - Multiple such calls to `mbedtls_mps_reader_feed()` are necessary
+ * to fulfill the original request, and the reader needs to do
+ * the necessary bookkeeping under the hood.
+ *
+ * This test comes in a few variants differing in the number and
+ * size of feed calls that the producer issues while the reader is
+ * accumulating the necessary data - see the comments below.
+ */
+
+ unsigned char bufA[100], bufB[100];
+ unsigned char *tmp;
+ unsigned char acc[70];
+ mbedtls_mps_reader rd;
+ mbedtls_mps_size_t fetch_len;
+ for( size_t i=0; (unsigned) i < sizeof( bufA ); i++ )
+ bufA[i] = (unsigned char) i;
+ for( size_t i=0; (unsigned) i < sizeof( bufB ); i++ )
+ bufB[i] = ~ ((unsigned char) i);
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufA, sizeof( bufA ) ) == 0 );
+
+ /* Consumption (upper layer) */
+ /* Ask for more than what's available. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 80, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 80, bufA, 80 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ /* 20 left, ask for 70 -> 50 overhead */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 70, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+
+ /* Preparation */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ switch( option )
+ {
+ case 0: /* 10 + 10 + 80 byte feed */
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB, 10 ) ==
+ MBEDTLS_ERR_MPS_READER_NEED_MORE );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB + 10, 10 ) ==
+ MBEDTLS_ERR_MPS_READER_NEED_MORE );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB + 20, 80 ) == 0 );
+ break;
+
+ case 1: /* 50 x 1byte */
+ for( size_t num_feed = 0; num_feed < 49; num_feed++ )
+ {
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB + num_feed, 1 ) ==
+ MBEDTLS_ERR_MPS_READER_NEED_MORE );
+ }
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB + 49, 1 ) == 0 );
+ break;
+
+ case 2: /* 49 x 1byte + 51bytes */
+ for( size_t num_feed = 0; num_feed < 49; num_feed++ )
+ {
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB + num_feed, 1 ) ==
+ MBEDTLS_ERR_MPS_READER_NEED_MORE );
+ }
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB + 49, 51 ) == 0 );
+ break;
+
+ default:
+ TEST_ASSERT( 0 );
+ break;
+ }
+
+ /* Consumption */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 70, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 20, bufA + 80, 20 );
+ ASSERT_COMPARE( tmp + 20, 50, bufB, 50 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 1000, &tmp, &fetch_len ) == 0 );
+ switch( option )
+ {
+ case 0:
+ TEST_ASSERT( fetch_len == 50 );
+ break;
+
+ case 1:
+ TEST_ASSERT( fetch_len == 0 );
+ break;
+
+ case 2:
+ TEST_ASSERT( fetch_len == 50 );
+ break;
+
+ default:
+ TEST_ASSERT( 0 );
+ break;
+ }
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+
+ /* Wrapup */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_reclaim_data_left( int option )
+{
+ /* This test exercises the behaviour of the MPS reader when a
+ * call to mbedtls_mps_reader_reclaim() is made before all data
+ * provided by the producer has been fetched and committed. */
+
+ unsigned char buf[100];
+ unsigned char *tmp;
+ mbedtls_mps_reader rd;
+ for( size_t i=0; (unsigned) i < sizeof( buf ); i++ )
+ buf[i] = (unsigned char) i;
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, NULL, 0 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, buf, sizeof( buf ) ) == 0 );
+
+ /* Consumption (upper layer) */
+ switch( option )
+ {
+ case 0:
+ /* Fetch (but not commit) the entire buffer. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, sizeof( buf ), &tmp, NULL )
+ == 0 );
+ ASSERT_COMPARE( tmp, 100, buf, 100 );
+ break;
+
+ case 1:
+ /* Fetch (but not commit) parts of the buffer. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, sizeof( buf ) / 2,
+ &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, sizeof( buf ) / 2, buf, sizeof( buf ) / 2 );
+ break;
+
+ case 2:
+ /* Fetch and commit parts of the buffer, then
+ * fetch but not commit the rest of the buffer. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, sizeof( buf ) / 2,
+ &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, sizeof( buf ) / 2, buf, sizeof( buf ) / 2 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, sizeof( buf ) / 2,
+ &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, sizeof( buf ) / 2,
+ buf + sizeof( buf ) / 2,
+ sizeof( buf ) / 2 );
+ break;
+
+ default:
+ TEST_ASSERT( 0 );
+ break;
+ }
+
+ /* Wrapup */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_DATA_LEFT );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_reclaim_data_left_retry()
+{
+ /* This test exercises the behaviour of the MPS reader when an attempt
+ * by the producer to reclaim the reader fails because of more data pending
+ * to be processed, and the consumer subsequently fetches more data. */
+ unsigned char buf[100];
+ unsigned char *tmp;
+ mbedtls_mps_reader rd;
+
+ for( size_t i=0; (unsigned) i < sizeof( buf ); i++ )
+ buf[i] = (unsigned char) i;
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, NULL, 0 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, buf, sizeof( buf ) ) == 0 );
+ /* Consumption (upper layer) */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 50, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 50, buf, 50 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 50, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 50, buf + 50, 50 );
+ /* Preparation */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_DATA_LEFT );
+ /* Consumption */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 50, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 50, buf + 50, 50 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ /* Wrapup */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_multiple_pausing( int option )
+{
+ /* This test exercises the behaviour of the MPS reader
+ * in the following situation:
+ * - A read request via `mbedtls_mps_reader_get()` can't
+ * be served and the reader is paused to accumulate
+ * the desired amount of data from the producer.
+ * - Once enough data is available, the consumer successfully
+ * reads the data from the reader, but afterwards exceeds
+ * the available data again - pausing is necessary for a
+ * second time.
+ */
+
+ unsigned char bufA[100], bufB[20], bufC[10];
+ unsigned char *tmp;
+ unsigned char acc[50];
+ mbedtls_mps_size_t tmp_len;
+ mbedtls_mps_reader rd;
+ for( size_t i=0; (unsigned) i < sizeof( bufA ); i++ )
+ bufA[i] = (unsigned char) i;
+ for( size_t i=0; (unsigned) i < sizeof( bufB ); i++ )
+ bufB[i] = ~ ((unsigned char) i);
+ for( size_t i=0; (unsigned) i < sizeof( bufC ); i++ )
+ bufC[i] = ~ ((unsigned char) i);
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufA, sizeof( bufA ) ) == 0 );
+
+ /* Consumption (upper layer) */
+ /* Ask for more than what's available. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 80, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 80, bufA, 80 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 80, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+
+ /* Preparation */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB, sizeof( bufB ) ) == 0 );
+
+ switch( option )
+ {
+ case 0: /* Fetch same chunks, commit afterwards, and
+ * then exceed bounds of new buffer; accumulator
+ * large enough. */
+
+ /* Consume */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, &tmp_len ) == 0 );
+ ASSERT_COMPARE( tmp, tmp_len, bufA + 80, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+
+ /* Prepare */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufC, sizeof( bufC ) ) == 0 );;
+
+ /* Consume */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufB + 10, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufC, 10 );
+ break;
+
+ case 1: /* Fetch same chunks, commit afterwards, and
+ * then exceed bounds of new buffer; accumulator
+ * not large enough. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 80, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 51, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+
+ /* Prepare */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
+ break;
+
+ case 2: /* Fetch same chunks, don't commit afterwards, and
+ * then exceed bounds of new buffer; accumulator
+ * large enough. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 80, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+
+ /* Prepare */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufC, sizeof( bufC ) ) == 0 );;
+
+ /* Consume */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 50, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 20, bufA + 80, 20 );
+ ASSERT_COMPARE( tmp + 20, 20, bufB, 20 );
+ ASSERT_COMPARE( tmp + 40, 10, bufC, 10 );
+ break;
+
+ case 3: /* Fetch same chunks, don't commit afterwards, and
+ * then exceed bounds of new buffer; accumulator
+ * not large enough. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 80, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 10, bufA + 90, 10 );
+ ASSERT_COMPARE( tmp + 10, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 21, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+
+ /* Prepare */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
+ break;
+
+ default:
+ TEST_ASSERT( 0 );
+ break;
+ }
+
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER:MBEDTLS_MPS_STATE_VALIDATION */
+void mbedtls_mps_reader_random_usage( int num_out_chunks,
+ int max_chunk_size,
+ int max_request,
+ int acc_size )
+
+{
+ /* Randomly pass a reader object back and forth between lower and
+ * upper layer and let each of them call the respective reader API
+ * functions in a random fashion.
+ *
+ * On the lower layer, we're tracking and concatenating
+ * the data passed to successful feed calls.
+ *
+ * For the upper layer, we track and concatenate buffers
+ * obtained from successful get calls.
+ *
+ * As long as the lower layer calls reclaim at least once, (resetting the
+ * fetched but not-yet-committed data), this should always lead to the same
+ * stream of outgoing/incoming data for the lower/upper layers, even if
+ * most of the random calls fail.
+ *
+ * NOTE: This test uses rand() for random data, which is not optimal.
+ * Instead, it would be better to get the random data from a
+ * static buffer. This both eases reproducibility and allows
+ * simple conversion to a fuzz target.
+ */
+ int ret;
+ unsigned char *acc = NULL;
+ unsigned char *outgoing = NULL, *incoming = NULL;
+ unsigned char *cur_chunk = NULL;
+ size_t cur_out_chunk, out_pos, in_commit, in_fetch;
+ int rand_op; /* Lower layer:
+ * - Reclaim (0)
+ * - Feed (1)
+ * Upper layer:
+ * - Get, do tolerate smaller output (0)
+ * - Get, don't tolerate smaller output (1)
+ * - Commit (2) */
+ int mode = 0; /* Lower layer (0) or Upper layer (1) */
+ int reclaimed = 1; /* Have to call reclaim at least once before
+ * returning the reader to the upper layer. */
+ mbedtls_mps_reader rd;
+
+ if( acc_size > 0 )
+ {
+ ASSERT_ALLOC( acc, acc_size );
+ }
+
+ /* This probably needs to be changed because we want
+ * our tests to be deterministic. */
+ // srand( time( NULL ) );
+
+ ASSERT_ALLOC( outgoing, num_out_chunks * max_chunk_size );
+ ASSERT_ALLOC( incoming, num_out_chunks * max_chunk_size );
+
+ mbedtls_mps_reader_init( &rd, acc, acc_size );
+
+ cur_out_chunk = 0;
+ in_commit = 0;
+ in_fetch = 0;
+ out_pos = 0;
+ while( cur_out_chunk < (unsigned) num_out_chunks )
+ {
+ if( mode == 0 )
+ {
+ /* Choose randomly between reclaim and feed */
+ rand_op = rand() % 2;
+
+ if( rand_op == 0 )
+ {
+ /* Reclaim */
+ ret = mbedtls_mps_reader_reclaim( &rd, NULL );
+
+ if( ret == 0 )
+ {
+ TEST_ASSERT( cur_chunk != NULL );
+ mbedtls_free( cur_chunk );
+ cur_chunk = NULL;
+ }
+ reclaimed = 1;
+ }
+ else
+ {
+ /* Feed reader with a random chunk */
+ unsigned char *tmp = NULL;
+ size_t tmp_size;
+ if( cur_out_chunk == (unsigned) num_out_chunks )
+ continue;
+
+ tmp_size = ( rand() % max_chunk_size ) + 1;
+ ASSERT_ALLOC( tmp, tmp_size );
+
+ TEST_ASSERT( mbedtls_test_rnd_std_rand( NULL, tmp, tmp_size ) == 0 );
+ ret = mbedtls_mps_reader_feed( &rd, tmp, tmp_size );
+
+ if( ret == 0 || ret == MBEDTLS_ERR_MPS_READER_NEED_MORE )
+ {
+ cur_out_chunk++;
+ memcpy( outgoing + out_pos, tmp, tmp_size );
+ out_pos += tmp_size;
+ }
+
+ if( ret == 0 )
+ {
+ TEST_ASSERT( cur_chunk == NULL );
+ cur_chunk = tmp;
+ }
+ else
+ {
+ mbedtls_free( tmp );
+ }
+
+ }
+
+ /* Randomly switch to consumption mode if reclaim
+ * was called at least once. */
+ if( reclaimed == 1 && rand() % 3 == 0 )
+ {
+ in_fetch = 0;
+ mode = 1;
+ }
+ }
+ else
+ {
+ /* Choose randomly between get tolerating fewer data,
+ * get not tolerating fewer data, and commit. */
+ rand_op = rand() % 3;
+ if( rand_op == 0 || rand_op == 1 )
+ {
+ mbedtls_mps_size_t get_size, real_size;
+ unsigned char *chunk_get;
+ get_size = ( rand() % max_request ) + 1;
+ if( rand_op == 0 )
+ {
+ ret = mbedtls_mps_reader_get( &rd, get_size, &chunk_get,
+ &real_size );
+ }
+ else
+ {
+ real_size = get_size;
+ ret = mbedtls_mps_reader_get( &rd, get_size, &chunk_get, NULL );
+ }
+
+ /* Check if output is in accordance with what was written */
+ if( ret == 0 )
+ {
+ memcpy( incoming + in_commit + in_fetch,
+ chunk_get, real_size );
+ TEST_ASSERT( memcmp( incoming + in_commit + in_fetch,
+ outgoing + in_commit + in_fetch,
+ real_size ) == 0 );
+ in_fetch += real_size;
+ }
+ }
+ else if( rand_op == 2 ) /* Commit */
+ {
+ ret = mbedtls_mps_reader_commit( &rd );
+ if( ret == 0 )
+ {
+ in_commit += in_fetch;
+ in_fetch = 0;
+ }
+ }
+
+ /* Randomly switch back to preparation */
+ if( rand() % 3 == 0 )
+ {
+ reclaimed = 0;
+ mode = 0;
+ }
+ }
+ }
+
+ /* Cleanup */
+ mbedtls_mps_reader_free( &rd );
+ mbedtls_free( incoming );
+ mbedtls_free( outgoing );
+ mbedtls_free( acc );
+ mbedtls_free( cur_chunk );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_reader_inconsistent_usage( int option )
+{
+ /* This test exercises the behaviour of the MPS reader
+ * in the following situation:
+ * - The consumer asks for more data than what's available
+ * - The reader is paused and receives more data from the
+ * producer until the original read request can be fulfilled.
+ * - The consumer does not repeat the original request but
+ * requests data in a different way.
+ *
+ * The reader does not guarantee that inconsistent read requests
+ * after pausing will succeed, and this test triggers some cases
+ * where the request fails.
+ */
+
+ unsigned char bufA[100], bufB[100];
+ unsigned char *tmp;
+ unsigned char acc[40];
+ mbedtls_mps_reader rd;
+ int success = 0;
+ for( size_t i=0; (unsigned) i < sizeof( bufA ); i++ )
+ bufA[i] = (unsigned char) i;
+ for( size_t i=0; (unsigned) i < sizeof( bufB ); i++ )
+ bufB[i] = ~ ((unsigned char) i);
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, acc, sizeof( acc ) );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufA, sizeof( bufA ) ) == 0 );
+ /* Consumption (upper layer) */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 80, &tmp, NULL ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 20, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+ /* Preparation */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, bufB, sizeof( bufB ) ) == 0 );
+ /* Consumption */
+ switch( option )
+ {
+ case 0:
+ /* Ask for buffered data in a single chunk, no commit */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 30, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 20, bufA + 80, 20 );
+ ASSERT_COMPARE( tmp + 20, 10, bufB, 10 );
+ success = 1;
+ break;
+
+ case 1:
+ /* Ask for buffered data in a single chunk, with commit */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 30, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 20, bufA + 80, 20 );
+ ASSERT_COMPARE( tmp + 20, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ success = 1;
+ break;
+
+ case 2:
+ /* Ask for more than was requested when pausing, #1 */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 31, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_INCONSISTENT_REQUESTS );
+ break;
+
+ case 3:
+ /* Ask for more than was requested when pausing #2 */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, (mbedtls_mps_size_t) -1, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_INCONSISTENT_REQUESTS );
+ break;
+
+ case 4:
+ /* Asking for buffered data in different
+ * chunks than before CAN fail. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 15, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 15, bufA + 80, 15 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 10, &tmp, NULL ) ==
+ MBEDTLS_ERR_MPS_READER_INCONSISTENT_REQUESTS );
+ break;
+
+ case 5:
+ /* Asking for buffered data different chunks
+ * than before NEED NOT fail - no commits */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 15, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 15, bufA + 80, 15 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 15, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 5, bufA + 95, 5 );
+ ASSERT_COMPARE( tmp + 5, 10, bufB, 10 );
+ success = 1;
+ break;
+
+ case 6:
+ /* Asking for buffered data different chunks
+ * than before NEED NOT fail - intermediate commit */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 15, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 15, bufA + 80, 15 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 15, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 5, bufA + 95, 5 );
+ ASSERT_COMPARE( tmp + 5, 10, bufB, 10 );
+ success = 1;
+ break;
+
+ case 7:
+ /* Asking for buffered data different chunks
+ * than before NEED NOT fail - end commit */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 15, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 15, bufA + 80, 15 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 15, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 5, bufA + 95, 5 );
+ ASSERT_COMPARE( tmp + 5, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ success = 1;
+ break;
+
+ case 8:
+ /* Asking for buffered data different chunks
+ * than before NEED NOT fail - intermediate & end commit */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 15, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 15, bufA + 80, 15 );
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 15, &tmp, NULL ) == 0 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ ASSERT_COMPARE( tmp, 5, bufA + 95, 5 );
+ ASSERT_COMPARE( tmp + 5, 10, bufB, 10 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+ success = 1;
+ break;
+
+ default:
+ TEST_ASSERT( 0 );
+ break;
+ }
+
+ if( success == 1 )
+ {
+ /* In all succeeding cases, fetch the rest of the second buffer. */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 90, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 90, bufB + 10, 90 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+
+ /* Wrapup */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ }
+
+ /* Wrapup */
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
+
+/* BEGIN_CASE depends_on:TEST_SUITE_MPS_READER */
+void mbedtls_mps_reader_feed_empty()
+{
+ /* This test exercises the behaviour of the reader when it is
+ * fed with a NULL buffer. */
+ unsigned char buf[100];
+ unsigned char *tmp;
+ mbedtls_mps_reader rd;
+ for( size_t i=0; (unsigned) i < sizeof( buf ); i++ )
+ buf[i] = (unsigned char) i;
+
+ /* Preparation (lower layer) */
+ mbedtls_mps_reader_init( &rd, NULL, 0 );
+
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, NULL, sizeof( buf ) ) ==
+ MBEDTLS_ERR_MPS_READER_INVALID_ARG );
+
+ /* Subsequent feed-calls should still succeed. */
+ TEST_ASSERT( mbedtls_mps_reader_feed( &rd, buf, sizeof( buf ) ) == 0 );
+
+ /* Consumption (upper layer) */
+ TEST_ASSERT( mbedtls_mps_reader_get( &rd, 100, &tmp, NULL ) == 0 );
+ ASSERT_COMPARE( tmp, 100, buf, 100 );
+ TEST_ASSERT( mbedtls_mps_reader_commit( &rd ) == 0 );
+
+ /* Wrapup */
+ TEST_ASSERT( mbedtls_mps_reader_reclaim( &rd, NULL ) == 0 );
+ mbedtls_mps_reader_free( &rd );
+}
+/* END_CASE */
diff --git a/visualc/VS2010/mbedTLS.vcxproj b/visualc/VS2010/mbedTLS.vcxproj
index 0fb1b5c..09c5341 100644
--- a/visualc/VS2010/mbedTLS.vcxproj
+++ b/visualc/VS2010/mbedTLS.vcxproj
@@ -251,6 +251,10 @@
<ClInclude Include="..\..\tests\include\test\drivers\test_driver.h" />
<ClInclude Include="..\..\library\check_crypto_config.h" />
<ClInclude Include="..\..\library\common.h" />
+ <ClInclude Include="..\..\library\mps_common.h" />
+ <ClInclude Include="..\..\library\mps_error.h" />
+ <ClInclude Include="..\..\library\mps_reader.h" />
+ <ClInclude Include="..\..\library\mps_trace.h" />
<ClInclude Include="..\..\library\psa_crypto_cipher.h" />
<ClInclude Include="..\..\library\psa_crypto_core.h" />
<ClInclude Include="..\..\library\psa_crypto_driver_wrappers.h" />
@@ -310,6 +314,8 @@
<ClCompile Include="..\..\library\md4.c" />
<ClCompile Include="..\..\library\md5.c" />
<ClCompile Include="..\..\library\memory_buffer_alloc.c" />
+ <ClCompile Include="..\..\library\mps_reader.c" />
+ <ClCompile Include="..\..\library\mps_trace.c" />
<ClCompile Include="..\..\library\net_sockets.c" />
<ClCompile Include="..\..\library\nist_kw.c" />
<ClCompile Include="..\..\library\oid.c" />