New functions psa_get_key_slot(), psa_get_empty_key_slot(),
psa_get_key_from_slot() to access a key slot object from a key slot
number. These functions perform all requisite validations:
* psa_get_key_slot() verifies that the key slot number is in range.
* psa_get_empty_key_slot() verifies that the slot is empty.
* psa_get_key_from_slot() verifies that the slot contains a key with
a suitable policy.
Always use these functions so as to make sure that the requisite
validations are always performed.
In psa_hash_finish and psa_mac_finish_internal, set the fallback
output length (which is reported on error) to the output buffer size,
not to the _expected_ buffer size which could be larger.
When the size of a buffer is 0, the corresponding pointer argument may
be null. In such cases, library functions must not perform arithmetic
on the pointer or call standard library functions such as memset and
memcpy, since that would be undefined behavior in C. Protect such
cases.
Refactor the storage of a 0-sized raw data object to make it store a
null pointer, rather than depending on the behavior of calloc(1,0).
The RSA module uses unsigned int for hash_length. The PSA Crypto API
uses size_t for hash_length. Cast hash_length to unsigned int when
passed to the hash module.
The GCM, CCM, RSA, and cipher modules inconsistently use int or unsigned
int for a count of bits. The PSA Crypto API uses size_t for counting
things. This causes issues on LLP64 systems where a size_t can hold more
than an unsigned int. Add casts for where key_bits and bits are passed to
mbedtls_* APIs.
Use size_t for block_size in psa_mac_abort() because
psa_get_hash_block_size() returns a size_t. This also helps to avoid
compiler warnings on LLP64 systems.
To avoid a possible loss of precision, and to be semantically correct,
use psa_key_slot_t (which is 16 bits) instead of size_t (which is 32 or
64 bits on common platforms) in mbedtls_psa_crypto_free().
Previously, the psa_set_key_lifetime() implementation did not match the
function declaration in psa/crypto.h. Value types don't need const,
since they are passed by value. Fix psa_set_key_lifetime()
implementation by making it match its declaration in the header.
This requires defining a maximum RSA key size, since the RSA key size
is the signature size. Enforce the maximum RSA key size when importing
or generating a key.
Fill the unused part of the output buffer with '!', for consistency
with hash and mac.
On error, set the output length to the output buffer size and fill the
output buffer with '!', again for consistency with hash and mac. This
way an invalid output is more visible in a memory dump.
Restructure the error paths so that there is a single place where the
unused part of the output buffer is filled.
Also remove a redundant initialization of *signature_length to 0.
Change the representation of an ECDSA signature from the ASN.1 DER
encoding used in TLS and X.509, to the concatenation of r and s
in big-endian order with a fixed size. A fixed size helps memory and
buffer management and this representation is generally easier to use
for anything that doesn't require the ASN.1 representation. This is
the same representation as PKCS#11 (Cryptoki) except that PKCS#11
allows r and s to be truncated (both to the same length), which
complicates the implementation and negates the advantage of a
fixed-size representation.
* Distinguish randomized ECDSA from deterministic ECDSA.
* Deterministic ECDSA needs to be parametrized by a hash.
* Randomized ECDSA only uses the hash for the initial hash step,
but add ECDSA(hash) algorithms anyway so that all the signature
algorithms encode the initial hashing step.
* Add brief documentation for the ECDSA signature mechanisms.
* Also define DSA signature mechanisms while I'm at it. There were
already key types for DSA.
* PSS needs to be parametrized by a hash.
* Don't use `_MGF1` in the names of macros for OAEP and PSS. No one
ever uses anything else.
* Add brief documentation for the RSA signature mechanisms.
Make psa_export_key() always set a valid data_length when exporting,
even when there are errors. This makes the API easier to use for buggy
programs (like our test code).
Our test code previously used exported_length uninitialized when
checking to see that the buffer returned was all zero in import_export()
in the case where an error was returned from psa_export_key().
Initialize exported_length to an invalid length, and check that it gets
set properly by psa_export_key(), to avoid this using export_length
uninitialized. Note that the mem_is_zero() check is still valid when
psa_export_key() returns an error, e.g. where exported_length is 0, as
we want to check that nothing was written to the buffer on error.
Out test code also previous passed NULL for the data_length parameter of
psa_export_key() when it expected a failure (in key_policy_fail()).
However, data_length is not allowed to be NULL, especially now that we
write to data_length from psa_export_key() even when there are errors.
Update the test code to not pass in a NULL data_length.
psa_hash_abort, psa_mac_abort and psa_cipher_abort now return
PSA_ERROR_BAD_STATE if operation->alg is obviously not valid, which
can only happen due to a programming error in the caller or in the
library. We can't detect all cases of calling abort on uninitialized
memory but this is dirt cheap and better than nothing.
It isn't used to define other macros and it doesn't seem that useful
for users. Remove it, we can reintroduce it if needed.
Define a similar function key_type_is_raw_bytes in the implementation
with a clear semantics: it's a key that's represented as a struct
raw_data.
Also add what was missing in the test suite to support block ciphers
with a block size that isn't 16.
Fix some buggy test data that passed only due to problems with DES
support in the product.
In psa_hash_start, psa_mac_start and psa_cipher_setup, return
PSA_ERROR_INVALID_ARGUMENT rather than PSA_ERROR_NOT_SUPPORTED when
the algorithm parameter is not the right category.
When psa_mac_start(), psa_encrypt_setup() or psa_cipher_setup()
failed, depending on when the failure happened, it was possible that
psa_mac_abort() or psa_cipher_abort() would crash because it would try
to call a free() function uninitialized data in the operation
structure. Refactor the functions so that they initialize the
operation structure before doing anything else.
Add non-regression tests and a few more positive and negative unit
tests for psa_mac_start() and psa_cipher_setup() (the latter via
psa_encrypt_setip()).
In psa_export_key, ensure that each byte of the output buffer either
contains its original value, is zero, or is part of the actual output.
Specifically, don't risk having partial output on error, and don't
leave extra data at the end of the buffer when exporting an asymmetric
key.
Test that exporting to a previously zeroed buffer leaves the buffer
zeroed outside the actual output if any.
Exporting an asymmetric key only worked if the target buffer had
exactly the right size, because psa_export_key uses
mbedtls_pk_write_key_der or mbedtls_pk_write_pubkey_der and these
functions write to the end of the buffer, which psa_export_key did not
correct for. Fix this by moving the data to the beginning of the
buffer if necessary.
Add non-regression tests.
psa_import_key must check that the imported key data matches the
expected key type. Implement the missing check for EC keys that the
curve is the expected one.
Avoid lines longer than 80 columns.
Remove some redundant parentheses, e.g. change
if( ( a == b ) && ( c == d ) )
to
if( a == b && c == d )
which makes lines less long and makes the remaining parentheses more
relevant.
Add missing parentheses around return statements.
There should be no semantic change in this commit.
Store the temporary key in the long-key case (where the key is first
hashed) directly into ipad. This reduces the stack usage a little, at
a slight cost in complexity.
In psa_mac_start, the hash of the key and ipad contain material that
can be used to make HMAC calculations with the key, therefore they
must be wiped.
In psa_mac_finish_internal, tmp contains an intermediate value which
could reveal the HMAC. This is definitely sensitive in the verify case,
and marginally sensitive in the finish case (it isn't if the hash
function is ideal, but it could make things worse if the hash function
is partially broken).
Split algorithm-specific code out of psa_mac_start. This makes the
function easier to read.
The behavior is mostly unchanged. In a few cases, errors before
setting a key trigger a context wipe where they didn't. This is a
marginal performance loss but only cases that are an error in caller
code.
* development-restricted: (578 commits)
Update library version number to 2.13.1
Don't define _POSIX_C_SOURCE in header file
Don't declare and define gmtime()-mutex on Windows platforms
Correct preprocessor guards determining use of gmtime()
Correct documentation of mbedtls_platform_gmtime_r()
Correct typo in documentation of mbedtls_platform_gmtime_r()
Correct POSIX version check to determine presence of gmtime_r()
Improve documentation of mbedtls_platform_gmtime_r()
platform_utils.{c/h} -> platform_util.{c/h}
Don't include platform_time.h if !MBEDTLS_HAVE_TIME
Improve wording of documentation of MBEDTLS_PLATFORM_GMTIME_R_ALT
Fix typo in documentation of MBEDTLS_PLATFORM_GMTIME_R_ALT
Replace 'thread safe' by 'thread-safe' in the documentation
Improve documentation of MBEDTLS_HAVE_TIME_DATE
ChangeLog: Add missing renamings gmtime -> gmtime_r
Improve documentation of MBEDTLS_HAVE_TIME_DATE
Minor documentation improvements
Style: Add missing period in documentation in threading.h
Rename mbedtls_platform_gmtime() to mbedtls_platform_gmtime_r()
Guard decl and use of gmtime mutex by HAVE_TIME_DATE and !GMTIME_ALT
...
Previous commits attempted to use `gmtime_s()` for IAR systems; however,
this attempt depends on the use of C11 extensions which lead to incompatibility
with other pieces of the library, such as the use of `memset()` which is
being deprecated in favor of `memset_s()` in C11.
Initial implementation for the AEAD APIs, missing the following:
* Concatenation of the tag to the output buffer.
* Updated documentation of the new functions.
* argument validations
* tests
Conflicts:
library/psa_crypto.c
tests/suites/test_suite_psa_crypto.data
tests/suites/test_suite_psa_crypto.function
All the conflicts are concurrent additions where the order doesn't
matter. I put the code from feature-psa (key policy) before the code
from PR #13 (key lifetime).
psa_get_key_lifetime() behavior changed regarding empty slots, now
it return the lifetime of and empty slots. Documentation in header
file updated accordingly.
Conflict resolution:
* `tests/suites/test_suite_psa_crypto.data`: in the new tests from PR #14,
rename `PSA_ALG_RSA_PKCS1V15_RAW` to `PSA_ALG_RSA_PKCS1V15_SIGN_RAW` as
was done in PR #15 in the other branch.
New header file crypto_struct.h. The main file crypto.sh declares
structures which are implementation-defined. These structures must be
defined in crypto_struct.h, which is included at the end so that the
structures can use types defined in crypto.h.
Implement psa_hash_start, psa_hash_update and psa_hash_final. This
should work for all hash algorithms supported by Mbed TLS, but has
only been smoke-tested for SHA-256, and only in the nominal case.
Don't use the pk module except as required for pkparse/pkwrite. The
PSA crypto layer is meant to work alongside pk, not on top of it.
Fix the compile-time dependencies on RSA/ECP handling in
psa_export_key, psa_destroy_key and psa_get_key_information.
Define psa_key_type_t and a first stab at a few values.
New functions psa_import_key, psa_export_key, psa_destroy_key,
psa_get_key_information. Implement them for raw data and RSA.
Under the hood, create an in-memory, fixed-size keystore with room
for MBEDTLS_PSA_KEY_SLOT_COUNT - 1 keys.
Add a new function mbedtls_rsa_get_bitlen which returns the RSA key
size, i.e. the bit size of the modulus. In the pk module, call
mbedtls_rsa_get_bitlen instead of mbedtls_rsa_get_len, which gave the
wrong result for key sizes that are not a multiple of 8.
This commit adds one non-regression test in the pk suite. More tests
are needed for RSA key sizes that are a multiple of 8.
This commit does not address RSA alternative implementations, which
only provide an interface that return the modulus size in bytes.
New module psa_crypto.c (MBEDTLS_PSA_CRYPTO_C):
Platform Security Architecture compatibility layer on top of
libmedcrypto.
Implement psa_crypto_init function which sets up a RNG.
Add a mbedtls_psa_crypto_free function which deinitializes the
library.
Define a first batch of error codes.
By the standard (RFC 6066, Sect. 4), the Maximum Fragment Length (MFL)
extension limits the maximum record payload size, but not the maximum
datagram size. However, not inferring any limitations on the MTU when
setting the MFL means that a party has no means to dynamically inform
the peer about MTU limitations.
This commit changes the function ssl_get_remaining_payload_in_datagram()
to never return more than
MFL - { Total size of all records within the current datagram }
thereby limiting the MTU to MFL + { Maximum Record Expansion }.
The function ssl_free_buffered_record() frees a future epoch record, if
such is present. Previously, it was called in mbedtls_handshake_free(),
i.e. an unused buffered record would be cleared at the end of the handshake.
This commit moves the call to the function ssl_buffering_free() responsible
for freeing all buffering-related data, and which is called not only at
the end of the handshake, but at the end of every flight. In particular,
future record epochs won't be buffered across flight boundaries anymore,
and they shouldn't.
The previous code appended messages to flights only if their handshake type,
as derived from the first byte in the message, was different from
MBEDTLS_SSL_HS_HELLO_REQUEST. This check should only be performed
for handshake records, while CCS records should immediately be appended.
In SSLv3, the client sends a NoCertificate alert in response to
a CertificateRequest if it doesn't have a CRT. This previously
lead to failure in ssl_write_handshake_msg() which only accepted
handshake or CCS records.
The previous code appended messages to flights only if their handshake type,
as derived from the first byte in the message, was different from
MBEDTLS_SSL_HS_HELLO_REQUEST. This check should only be performed
for handshake records, while CCS records should immediately be appended.
In SSLv3, the client sends a NoCertificate alert in response to
a CertificateRequest if it doesn't have a CRT. This previously
lead to failure in ssl_write_handshake_msg() which only accepted
handshake or CCS records.
Previous commits introduced the field `total_bytes_buffered`
which is supposed to keep track of the cumulative size of
all heap allocated buffers used for the purpose of reassembly
and/or buffering of future messages.
However, the buffering of future epoch records were not reflected
in this field so far. This commit changes this, adding the length
of a future epoch record to `total_bytes_buffered` when it's buffered,
and subtracting it when it's freed.
This commit adds a static function ssl_buffer_make_space() which
takes a buffer size as an argument and attempts to free as many
future message bufffers as necessary to ensure that the desired
amount of buffering space is available without violating the
total buffering limit set by MBEDTLS_SSL_DTLS_MAX_BUFFERING.
If the next expected handshake message can't be reassembled because
buffered future messages have already used up too much of the available
space for buffering, free those future message buffers in order to
make space for the reassembly, starting with the handshake message
that's farthest in the future.
This commit adds a static function ssl_buffering_free_slot()
which allows to free a particular structure used to buffer
and/or reassembly some handshake message.
This commit introduces a compile time constant MBEDTLS_SSL_DTLS_MAX_BUFFERING
to mbedtls/config.h which allows the user to control the cumulative size of
all heap buffer allocated for the purpose of reassembling and buffering
handshake messages.
It is put to use by introducing a new field `total_bytes_buffered` to
the buffering substructure of `mbedtls_ssl_handshake_params` that keeps
track of the total size of heap allocated buffers for the purpose of
reassembly and buffering at any time. It is increased whenever a handshake
message is buffered or prepared for reassembly, and decreased when a
buffered or fully reassembled message is copied into the input buffer
and passed to the handshake logic layer.
This commit does not yet include future epoch record buffering into
account; this will be done in a subsequent commit.
Also, it is now conceivable that the reassembly of the next expected
handshake message fails because too much buffering space has already
been used up for future messages. This case currently leads to an
error, but instead, the stack should get rid of buffered messages
to be able to buffer the next one. This will need to be implemented
in one of the next commits.
A previous commit introduced the function ssl_prepare_reassembly_buffer()
which took a message length and a boolean flag indicating if a reassembly
bit map was needed, and attempted to heap-allocate a buffer of sufficient
size to hold both the message, its header, and potentially the reassembly
bitmap.
A subsequent commit is going to introduce a limit on the amount of heap
allocations allowed for the purpose of buffering, and this change will
need to know the reassembly buffer size before attempting the allocation.
To this end, this commit changes ssl_prepare_reassembly_buffer() into
ssl_get_reassembly_buffer_size() which solely computes the reassembly
buffer size, and performing the heap allocation manually in
ssl_buffer_message().
This commit moves the length and content check for CCS messages to
the function mbedtls_ssl_handle_message_type() which is called after
a record has been deprotected.
Previously, these checks were performed in the function
mbedtls_ssl_parse_change_cipher_spec(); however, now that
the arrival of out-of-order CCS messages is remembered
as a boolean flag, the check also has to happen when this
flag is set. Moving the length and content check to
mbedtls_ssl_handle_message_type() allows to treat both
checks uniformly.
Depends on the current transform, which might change when retransmitting a
flight containing a Finished message, so compute it only after the transform
is swapped.
This setting belongs to the individual connection, not to a configuration
shared by many connections. (If a default value is desired, that can be handled
by the application code that calls mbedtls_ssl_set_mtu().)
There are at least two ways in which this matters:
- per-connection settings can be adjusted if MTU estimates become available
during the lifetime of the connection
- it is at least conceivable that a server might recognize restricted clients
based on range of IPs and immediately set a lower MTU for them. This is much
easier to do with a per-connection setting than by maintaining multiple
near-duplicated ssl_config objects that differ only by the MTU setting.
The SSL context is passed to the reassembly preparation function
ssl_prepare_reassembly_buffer() solely for the purpose of allowing
debugging output. This commit marks the context as unused if
debugging is disabled (through !MBEDTLS_DEBUG_C).
This commit implements the buffering of a record from the next epoch.
- The buffering substructure of mbedtls_ssl_handshake_params
gets another field to hold a raw record (incl. header) from
a future epoch.
- If ssl_parse_record_header() sees a record from the next epoch,
it signals that it might be suitable for buffering by returning
MBEDTLS_ERR_SSL_EARLY_MESSAGE.
- If ssl_get_next_record() finds this error code, it passes control
to ssl_buffer_future_record() which may or may not decide to buffer
the record; it does so if
- a handshake is in progress,
- the record is a handshake record
- no record has already been buffered.
If these conditions are met, the record is backed up in the
aforementioned buffering substructure.
- If the current datagram is fully processed, ssl_load_buffered_record()
is called to check if a record has been buffered, and if yes,
if by now the its epoch is the current one; if yes, it copies
the record into the (empty! otherwise, ssl_load_buffered_record()
wouldn't have been called) input buffer.
This commit implements future handshake message buffering
and loading by implementing ssl_load_buffered_message()
and ssl_buffer_message().
Whenever a handshake message is received which is
- a future handshake message (i.e., the sequence number
is larger than the next expected one), or which is
- a proper fragment of the next expected handshake message,
ssl_buffer_message() is called, which does the following:
- Ignore message if its sequence number is too far ahead
of the next expected sequence number, as controlled by
the macro constant MBEDTLS_SSL_MAX_BUFFERED_HS.
- Otherwise, check if buffering for the message with the
respective sequence number has already commenced.
- If not, allocate space to back up the message within
the buffering substructure of mbedtls_ssl_handshake_params.
If the message is a proper fragment, allocate additional
space for a reassembly bitmap; if it is a full message,
omit the bitmap. In any case, fall throuh to the next case.
- If the message has already been buffered, check that
the header is the same, and add the current fragment
if the message is not yet complete (this excludes the
case where a future message has been received in a single
fragment, hence omitting the bitmap, and is afterwards
also received as a series of proper fragments; in this
case, the proper fragments will be ignored).
For loading buffered messages in ssl_load_buffered_message(),
the approach is the following:
- Check the first entry in the buffering window (the window
is always based at the next expected handshake message).
If buffering hasn't started or if reassembly is still
in progress, ignore. If the next expected message has been
fully received, copy it to the input buffer (which is empty,
as ssl_load_buffered_message() is only called in this case).
This commit returns the error code MBEDTLS_ERR_SSL_EARLY_MESSAGE
for proper handshake fragments, forwarding their treatment to
the buffering function ssl_buffer_message(); currently, though,
this function does not yet buffer or reassembly HS messages, so:
! This commit temporarily disables support for handshake reassembly !
This commit introduces helper functions
- ssl_get_hs_frag_len()
- ssl_get_hs_frag_off()
to parse the fragment length resp. fragment offset fields
in the handshake header.
Moreover, building on these helper functions, it adds a
function ssl_check_hs_header() checking the validity of
a DTLS handshake header with respect to the specification,
i.e. the indicated fragment must be a subrange of the total
handshake message, and the total handshake fragment length
(including header) must not exceed the record content size.
These checks were previously performed at a later stage during
ssl_reassemble_dtls_handshake().
This commit introduces a static helper function ssl_get_hs_total_len()
parsing the total message length field in the handshake header, and
puts it to use in mbedtls_ssl_prepare_handshake_record().
This commit introduces, but does not yet put to use, a sub-structure
of mbedtls_ssl_handshake_params::buffering that will be used for the
buffering and/or reassembly of handshake messages with handshake
sequence numbers that are greater or equal to the next expected
sequence number.
This commit introduces a sub-structure `buffering` within
mbedtls_ssl_handshake_params that shall contain all data
related to the reassembly and/or buffering of handshake
messages.
Currently, only buffering of CCS messages is implemented,
so the only member of this struct is the previously introduced
`seen_ccs` field.
This commit introduces a static function ssl_hs_is_proper_fragment()
to check if the current incoming handshake message is a proper fragment.
It is used within mbedtls_ssl_prepare_handshake_record() to decide whether
handshake reassembly through ssl_reassemble_dtls_handshake() is needed.
The commit changes the behavior of the library in the (unnatural)
situation where proper fragments for a handshake message are followed
by a non-fragmented version of the same message. In this case,
the previous code invoked the handshake reassembly routine
ssl_reassemble_dtls_handshake(), while with this commit, the full
handshake message is directly forwarded to the user, no altering
the handshake reassembly state -- in particular, not freeing it.
As a remedy, freeing of a potential handshake reassembly structure
is now done as part of the handshake update function
mbedtls_ssl_update_handshake_status().
This commit adds a parameter to ssl_prepare_reassembly_buffer()
allowing to disable the allocation of space for a reassembly bitmap.
This will allow this function to be used for the allocation of buffers
for future handshake messages in case these need no fragmentation.
This commit moves the code-path preparing the handshake
reassembly buffer, consisting of header, message content,
and reassembly bitmap, to a separate function
ssl_prepare_reassembly_buffer().
This leads future HS messages to traverse the buffering
function ssl_buffer_message(), which however doesn't do
anything at the moment for HS messages. Since the error
code MBEDTLS_ERR_SSL_EARLY_MESSAGE is afterwards remapped
to MBEDTLS_ERR_SSL_CONTINUE_PROCESSING -- which is what
was returned prior to this commit when receiving a future
handshake message -- this commit therefore does not yet
introduce any change in observable behavior.
This commit implements support for remembering out-of-order
CCS messages. Specifically, a flag is set whenever a CCS message
is read which remains until the end of a flight, and when a
CCS message is expected and a CCS message has been seen in the
current flight, a synthesized CCS record is created.
This commit introduces a function ssl_record_is_in_progress()
to indicate if there is there is more data within the current
record to be processed. Further, it moves the corresponding
call from ssl_read_record_layer() to the parent function
mbedtls_ssl_read_record(). With this change, ssl_read_record_layer()
has the sole purpose of fetching and decoding a new record,
and hence this commit also renames it to ssl_get_next_record().
Subsequent commits will potentially inject buffered
messages after the last incoming message has been
consumed, but before a new one is fetched. As a
preparatory step to this, this commit moves the call
to ssl_consume_current_message() from ssl_read_record_layer()
to the calling function mbedtls_ssl_read_record().
The first part of the function ssl_read_record_layer() was
to mark the previous message as consumed. This commit moves
the corresponding code-path to a separate static function
ssl_consume_current_message().
This function was previously global because it was
used directly within ssl_parse_certificate_verify()
in library/ssl_srv.c. The previous commit removed
this dependency, replacing the call by a call to
the global parent function mbedtls_ssl_read_record().
This renders mbedtls_ssl_read_record_layer() internal
and therefore allows to make it static, and accordingly
rename it as ssl_read_record_layer().
Usually, debug messages beginning with "=> and "<="
match up and indicate entering of and returning from
functions, respectively. This commit fixes one exception
to this rule in mbedtls_ssl_read_record(), which sometimes
printed two messages of the form "<= XXX".
Previously, mbedtls_ssl_read_record() always updated the handshake
checksum in case a handshake record was received. While desirable
most of the time, for the CertificateVerify message the checksum
update must only happen after the message has been fully processed,
because the validation requires the handshake digest up to but
excluding the CertificateVerify itself. As a remedy, the bulk
of mbedtls_ssl_read_record() was previously duplicated within
ssl_parse_certificate_verify(), hardening maintenance in case
mbedtls_ssl_read_record() is subject to changes.
This commit adds a boolean parameter to mbedtls_ssl_read_record()
indicating whether the checksum should be updated in case of a
handshake message or not. This allows using it also for
ssl_parse_certificate_verify(), manually updating the checksum
after the message has been processed.
This for example lead to the following corner case bug:
The code attempted to piggy-back a Finished message at
the end of a datagram where precisely 12 bytes of payload
were still available. This lead to an empty Finished fragment
being sent, and when mbedtls_ssl_flight_transmit() was called
again, it believed that it was just starting to send the
Finished message, thereby calling ssl_swap_epochs() which
had already happened in the call sending the empty fragment.
Therefore, the second call would send the 'rest' of the
Finished message with wrong epoch.
This commit adds a public function
`mbedtls_ssl_conf_datagram_packing()`
that allows to allow / forbid the packing of multiple
records within a single datagram.
The `partial` argument is only used when DTLS and same port
client reconnect are enabled. This commit marks the variable
as unused if that's not the case.
If neither the maximum fragment length extension nor DTLS
are used, the SSL context argument is unnecessary as the
maximum payload length is hardcoded as MBEDTLS_SSL_MAX_CONTENT_LEN.
This commit finally enables datagram packing by modifying the
record preparation function ssl_write_record() to not always
calling mbedtls_ssl_flush_output().
The packing of multiple records within a single datagram works
by increasing the pointer `out_hdr` (pointing to the beginning
of the next outgoing record) within the datagram buffer, as
long as space is available and no flush was mandatory.
This commit does not yet change the code's behavior of always
flushing after preparing a record, but it introduces the logic
of increasing `out_hdr` after preparing the record, and resetting
it after the flush has been completed.
Previously, the record sequence number was incremented at the
end of each successful call to mbedtls_ssl_flush_output(),
which works as long as there is precisely one such call for
each outgoing record.
When packing multiple records into a single datagram, this
property is no longer true, and instead the increment of the
record sequence number must happen after the record has been
prepared, and not after it has been dispatched.
This commit moves the code for incrementing the record sequence
number from mbedtls_ssl_flush_output() to ssl_write_record().
This commit is another step towards supporting the packing of
multiple records within a single datagram.
Previously, the incremental outgoing record sequence number was
statically stored within the record buffer, at its final place
within the record header. This slightly increased efficiency
as it was not necessary to copy the sequence number when writing
outgoing records.
When allowing multiple records within a single datagram, it is
necessary to allow the position of the current record within the
datagram buffer to be flexible; in particular, there is no static
address for the record sequence number field within the record header.
This commit introduces an additional field `cur_out_ctr` within
the main SSL context structure `mbedtls_ssl_context` to keep track
of the outgoing record sequence number independent of the buffer used
for the current record / datagram. Whenever a new record is written,
this sequence number is copied to the the address `out_ctr` of the
sequence number header field within the current outgoing record.
The SSL/TLS module maintains a number of internally used pointers
`out_hdr`, `out_len`, `out_iv`, ..., indicating where to write the
various parts of the record header.
These pointers have to be kept in sync and sometimes need update:
Most notably, the `out_msg` pointer should always point to the
beginning of the record payload, and its offset from the pointer
`out_iv` pointing to the end of the record header is determined
by the length of the explicit IV used in the current record
protection mechanism.
This commit introduces functions deducing these pointers from
the pointers `out_hdr` / `in_hdr` to the beginning of the header
of the current outgoing / incoming record.
The flexibility gained by these functions will subsequently
be used to allow shifting of `out_hdr` for the purpose of
packing multiple records into a single datagram.
For now, just check that it causes us to fragment. More tests are coming in
follow-up commits to ensure we respect the exact value set, including when
renegotiating.
Note: no interop tests in ssl-opt.sh for now, as some of them make us run into
bugs in (the CI's default versions of) OpenSSL and GnuTLS, so interop tests
will be added later once the situation is clarified. <- TODO
This will allow fragmentation to always happen in the same place, always from
a buffer distinct from ssl->out_msg, and with the same way of resuming after
returning WANT_WRITE
- take advantage of the fact that we're only called for first send
- put all sanity checks at the top
- rename and constify shortcut variables
- improve comments