blob: 427c1bd254f9e605e95dd86cfe5b765c7191e159 [file] [log] [blame]
/*
* 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 */