From: Matt Caswell Date: Tue, 8 Nov 2022 14:52:55 +0000 (+0000) Subject: Update the record layer design based on implementation experience X-Git-Tag: openssl-3.2.0-alpha1~1703 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=48cc4e0c2046624c28d431ac51cdfce1a6e6a597;p=thirdparty%2Fopenssl.git Update the record layer design based on implementation experience Reviewed-by: Tomas Mraz Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/17969) --- diff --git a/doc/designs/quic-design/record-layer.md b/doc/designs/quic-design/record-layer.md index 0f952127a21..a68c4690001 100644 --- a/doc/designs/quic-design/record-layer.md +++ b/doc/designs/quic-design/record-layer.md @@ -1,21 +1,19 @@ Design Problem: Abstract Record Layer ===================================== -This document covers the design of an abstract record layer for use in (D)TLS -and QUIC. +This document covers the design of an abstract record layer for use in (D)TLS. +The QUIC record layer is handled separately. A record within this document refers to a packet of data. It will typically -contain some form of header data and some payload data, and will often be +contain some header data and some payload data, and will often be cryptographically protected. A record may or may not have a one-to-one -correspondence with network packets, depending on the protocol and the -implementation details of an individual record layer. +correspondence with network packets, depending on the implementation details of +an individual record layer. -The term record comes from the TLS and DTLS specifications. It is not used in -QUIC. A TLS record is roughly analagous to a QUIC packet. For the sake of -simplicity in this document we use the term record to refer to both concepts. +The term record comes directly from the TLS and DTLS specifications. -Libssl in OpenSSL 3.0 supports a number of different types of record layer, -and record layer variants: +Libssl supports a number of different types of record layer, and record layer +variants: - Standard TLS record layer - Standard DTLS record layer @@ -23,19 +21,19 @@ and record layer variants: Within the TLS record layer there are options to handle "multiblock" and "pipelining" which are different approaches for supporting the reading or -writing of multiple records at the same time. +writing of multiple records at the same time. All record layer variants also +have to be able to handle different protocol versions. -These different record layer implementations and variants have each been added -at different times and over many years. The result is that each have taken -slightly different approaches for achieving the goals that were appropriate at -the time and the integration points where they have been added are spread -throughout the code. +These different record layer implementations, variants and protocol versions +have each been added at different times and over many years. The result is that +each took slightly different approaches for achieving the goals that were +appropriate at the time and the integration points where they were added were +spread throughout the code. -The introduction of QUIC support will see the implementation of two more record -layers: -- QUIC record layer: A record here refers to a QUIC packet -- QUIC-TLS record layer: This refers to the "inner" TLS implementation used by - QUIC. Records here will be QUIC CRYPTO frames. +The introduction of QUIC support will see the implementation of a new record +layer, i.e. the QUIC-TLS record layer. This refers to the "inner" TLS +implementation used by QUIC. Records here will be in the form of QUIC CRYPTO +frames. Requirements ------------ @@ -61,15 +59,17 @@ lists these requirements that are relevant to the record layer: it should be possible for external libraries to be able to use the pluggable record layer interface and it should offer a stable ABI (via a provider). -* The internal architecture should allow for the fact that we may want to - support "single copy" APIs in the future - The MVP requirements are: * a pluggable record layer (not public for MVP) -Candidate Solution: Use a METHOD based approach ------------------------------------------------- +Candidate Solutions that were considered +---------------------------------------- + +This section outlines two different solution approaches that were considered for +the abstract record layer + +### Use a METHOD based approach A METHOD based approach is simply a structure containing function pointers. It is a common pattern in the OpenSSL codebase. Different strategies for @@ -78,8 +78,8 @@ the caller of the METHOD. In this solution we would seek to implement a different METHOD for each of the types of record layer that we support, i.e. there would be one for the standard -TLS record layer, one for the standard DTLS record layer, one for kernel TLS, -one for QUIC and one for QUIC-TLS. +TLS record layer, one for the standard DTLS record layer, one for kernel TLS and +one for QUIC-TLS. In the MVP the METHOD approach would be private. However, once it has stabilised, it would be straight forward to supply public functions to enable @@ -91,6 +91,7 @@ the MVP could implement a METHOD based approach, and subsequent releases could convert the METHODs into fully fetchable algorithms. Pros: + * Simple approach that has been used historically in OpenSSL * Could be used as the basis for the final public solution * Could also be used as the basis for a fetchable solution in a subsequent @@ -100,13 +101,13 @@ Pros: later release Cons: + * Not consistent with the provider based approach we used for extensibility in 3.0 * If this option is implemented and later converted to a fetchable solution then some rework might be required -Candidate Solution: Use a provider based approach -------------------------------------------------- +### Use a provider based approach This approach is very similar to the alternative METHOD based approach. The main difference is that the record layer implementations would be held in @@ -122,22 +123,30 @@ functions that can be implemented. Additionally implementing the infrastructure for a new fetchable operation is more involved than a METHOD based approach. Pros: + * Consistent with the extensibility solution used in 3.0 * If this option is implemented immediately in the MVP then it would avoid later rework if adopted in a subsequent release Cons: + * More complicated to implement than the simple METHOD based approach * Cannot pass complex objects across the provider boundary +### Selected solution + +The METHOD based approach has been selected for MVP, with the expectation that +subsequent releases will convert it to a full provider based solution accessible +to third party applications. -Solution Outline: Use a METHOD based approach ---------------------------------------------- +Solution Description: The METHOD based approach +----------------------------------------------- -This section focuses on the "Use a METHOD based approach" candidate solution -above and further elaborates a design for how that approach might work. +This section focuses on the selected approach of using METHODs and further +elaborates on how the design works. -A proposed internal record method API is given in [Appendix A](#appendix-a). +A proposed internal record method API is given in +[Appendix A](#appendix-a-the-internal-record-method-api). An `OSSL_RECORD_METHOD` represents the implementation of a particular type of record layer. It contains a set of function pointers to represent the various @@ -157,56 +166,40 @@ the kernel TLS record layer once the handshake is complete. A new `OSSL_RECORD_LAYER` is created by calling the `new` function of the associated `OSSL_RECORD_METHOD`, and freed by calling the `free` function. The -internal structure details of an `OSSL_RECORD_LAYER` are entirely hidden to -libssl and will be specific to the given `OSSL_RECORD_METHOD`. - -The payload for a record that will be written out to the network will be -assembled by libssl. That payload may be spread across multiple buffers. For -example a QUIC record (packet) may consist of multiple frames. There might be a -buffer containing the header data for the first frame, followed by a second -buffer containing the contents of the frame. A third buffer might contain the -header data for a second frame, and a fourth buffer might contain the payload -data for the second frame. There can be an arbitrary number of buffers which, -when concatenated together, form the total payload for the whole record. This -approach means that libssl can avoid having to copy all of the data from -multiple sources into a single buffer before calling the record layer. - -All of the above data for a single record will be represented by an -`OSSL_RECORD_TEMPLATE` structure. - -In order to assemble a record, libssl will need to know the maximum length of a -record that can be supported by the `OSSL_RECORD_LAYER`. In order to support -this an `OSSL_RECORD_METHOD` will supply a `get_max_record_len()` function to -query this value. It will be libssl's responsibility to ensure that no record -exceeds the maximum supported record length. - -An `OSSL_RECORD_METHOD` supplies a `write_records` function which libssl can -call to write one or more records. Libssl will supply an array of -`OSSL_RECORD_TEMPLATE` objects along with the number of such templates. This -number is guaranteed to never be greater than the maximum number of records -that the record layer can handle at one time as returned by the -`get_max_records()` function. +parameters to the `new` function also supply all of the cryptographic state +(e.g. keys, ivs, symmetric encryption algorithms, hash algorithm etc) used by +the record layer. The internal structure details of an `OSSL_RECORD_LAYER` are +entirely hidden to the rest of libssl and can be specific to the given +`OSSL_RECORD_METHOD`. In practice the standard internal TLS, DTLS and KTLS +`OSSL_RECORD_METHOD`s all use a common `OSSL_RECORD_LAYER` structure. However +the QUIC-TLS implementation is likely to use a different structure layout. + +All of the header and payload data for a single record will be represented by an +`OSSL_RECORD_TEMPLATE` structure when writing. Libssl will construct a set of +templates for records to be written out and pass them to the "write" record +layer. In most cases only a single record is ever written out at one time, +however there are some cases (such as when using the "pipelining" or +"multibuffer" optimisations) that multiple records can be written in one go. + +It is the record layer's responsibility to know whether it can support multiple +records in one go or not. It is libssl's responsibility to split the payload +data into `OSSL_RECORD_TEMPLATE` objects. Libssl will call the record layer's +`get_max_records()` function to determine how many records a given payload +should be split into. If that value is more than one, then libssl will construct +(up to) that number of `OSSL_RECORD_TEMPLATE`s and pass the whole set to the +record layer's `write_records()` function. The implementation of the `write_records` function must construct the appropriate number of records, apply protection to them as required and then -write them out to the underlying transport layer BIO. Congestion or flow control -limits may apply. The maximum amount of data that may be sent at the current -time is supplied by libssl in the `allowance` parameter. It is the -`OSSL_RECORD_METHOD`'s responsibily to ensure that no more bytes than -`allowance` are transmitted via the transport layer BIO. In the event that not -all the data can be transmitted at the current time (either because of the -`allowance` limit, or because the underlying transport has indicated a retry), -then the `write_records` function will return a "retry" response. It is -permissible for the data to be partially sent, but this is still considered a -"retry" until all of the data is sent. The `sent` parameter will be filled in -with the number of bytes sent during this `write_records` call in both a -success and a retry response. +write them out to the underlying transport layer BIO. In the event that not +all the data can be transmitted at the current time (e.g. because the underlying +transport has indicated a retry), then the `write_records` function will return +a "retry" response. It is permissible for the data to be partially sent, but +this is still considered a "retry" until all of the data is sent. On a success or retry response libssl may free its buffers immediately. The `OSSL_RECORD_LAYER` object will have to buffer any untransmitted data until it -is eventually sent. The move of data from the input buffers to the internal -`OSSL_RECORD_METHOD` buffer should occur during packet protection and is the -"single copy" allowed by the requirements. +is eventually sent. If a "retry" occurs, then libssl will subsequently call `retry_write_records` and continue to do so until a success return value is received. Libssl will @@ -223,23 +216,81 @@ data contained in it. Each record has an associated opaque handle `rechandle`. The record data must remain buffered by the `OSSL_RECORD_LAYER` until it has been released via a call to `release_record()`. +A record layer implementation supplies various functions to enable libssl to +query the current state. In particular: -Appendix A: An internal record method API ----------------------------------------------------------------- -A proposed internal recordmethod.h header file for the record method API: +`unprocessed_read_pending()`: to query whether there is data buffered that has +already been read from the underlying BIO, but not yet processed. -```` C -/* - * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ +`processed_read_pending()`: to query whether there is data buffered that has +been read from the underlying BIO and has been processed. The data is not +necessarily application data. + +`app_data_pending()`: to query the amount of processed application data that is +buffered and available for immediate read. + +`get_alert_code()`: to query the alert code that should be used in the event +that a previous attempt to read or write records failed. + +`get_state()`: to obtain a printable string to describe the current state of the +record layer. + +`get_compression()`: to obtain information about the compression method +currently being used by the record layer. + +`get_max_record_overhead()`: to obtain the maximum amount of bytes the record +layer will add to the payload bytes before transmission. This does not include +any expansion that might occur during compression. Currently this is only +implemented for DTLS. + +In addition, libssl will tell the record layer about various events that might +occur that are relevant to the record layer's operation: + +`set1_bio()`: called if the underlying BIO being used by the record layer has +been changed. + +`set_protocol_version()`: called during protocol version negotiation when a +specific protocol version has been selected. + +`set_plain_alerts()`: to indicate that receiving unencrypted alerts is allowed +in the current context, even if normally we would expect to receive encrypted +data. This is only relevant for TLSv1.3. + +`set_first_handshake()`: called at the beginning and end of the first handshake +for any given (D)TLS connection. -#include +`set_max_pipelines()`: called to configure the maximum number of pipelines of +data that the record layer should process in one go. By default this is 1. +`set_in_init()`: called by libssl to tell the record layer whether we are +currently `in_init` or not. Defaults to "true". + +`set_options()`: called by libssl in the event that the current set of options +to use has been updated. + +`set_max_frag_len()`: called by libssl to set the maximum allowed fragment +length that is in force at the moment. This might be the result of user +configuration, or it may be negotiated during the handshake. + +`increment_sequence_ctr()`: force the record layer to increment its sequence +counter. In most cases the record layer will entirely manage its own sequence +counters. However in the DTLSv1_listen() corner case, libssl needs to initialise +the record layer with an incremented sequence counter. + +`alloc_buffers()`: called by libssl to request that the record layer allocate +its buffers. This is a hint only and the record layer is expected to manage its +own buffer allocation and freeing. + +`free_buffers()`: called by libssl to request that the record layer free its +buffers. This is a hint only and the record layer is expected to manage its own +buffer allocation and freeing. + +Appendix A: The internal record method API +------------------------------------------ + +The internal recordmethod.h header file for the record method API: + +```` C /* * We use the term "record" here to refer to a packet of data. Records are * typically protected via a cipher and MAC, or an AEAD cipher (although not @@ -250,27 +301,6 @@ A proposed internal recordmethod.h header file for the record method API: * refer to both contexts. */ - -/* - * Types of QUIC record layer; - * - * QUIC reuses the TLS handshake for agreeing secrets. An SSL object representing - * a QUIC connection will have an additional SSL object internally representing - * the TLS state of the QUIC handshake. This internal TLS is referred to as - * QUIC-TLS in this file. - * "Records" output from QUIC-TLS contains standard TLS handshake messages and - * are *not* encrypted directly but are instead wrapped up in plaintext - * CRYPTO frames. These CRYPTO frames could be collected together with other - * QUIC frames into a single QUIC packet. The QUIC record layer will then - * encrypt the whole packet. - * - * So we have: - * QUIC-TLS record layer: outputs plaintext CRYPTO frames containing TLS - * handshake messages only. - * QUIC record layer: outputs encrypted packets which may contain CRYPTO frames - * or any other type of QUIC frame. - */ - /* * An OSSL_RECORD_METHOD is a protcol specific method which provides the * functions for reading and writing records for that protocol. Which @@ -285,33 +315,36 @@ typedef struct ossl_record_method_st OSSL_RECORD_METHOD; typedef struct ossl_record_layer_st OSSL_RECORD_LAYER; -#define OSSL_RECORD_ROLE_CLIENT 0 -#define OSSL_RECORD_ROLE_SERVER 1 +# define OSSL_RECORD_ROLE_CLIENT 0 +# define OSSL_RECORD_ROLE_SERVER 1 -#define OSSL_RECORD_DIRECTION_READ 0 -#define OSSL_RECORD_DIRECTION_WRITE 1 +# define OSSL_RECORD_DIRECTION_READ 0 +# define OSSL_RECORD_DIRECTION_WRITE 1 /* * Protection level. For <= TLSv1.2 only "NONE" and "APPLICATION" are used. */ -#define OSSL_RECORD_PROTECTION_LEVEL_NONE 0 -#define OSSL_RECORD_PROTECTION_LEVEL_EARLY 1 -#define OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE 2 -#define OSSL_RECORD_PROTECTION_LEVEL_APPLICATION 3 +# define OSSL_RECORD_PROTECTION_LEVEL_NONE 0 +# define OSSL_RECORD_PROTECTION_LEVEL_EARLY 1 +# define OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE 2 +# define OSSL_RECORD_PROTECTION_LEVEL_APPLICATION 3 +# define OSSL_RECORD_RETURN_SUCCESS 1 +# define OSSL_RECORD_RETURN_RETRY 0 +# define OSSL_RECORD_RETURN_NON_FATAL_ERR -1 +# define OSSL_RECORD_RETURN_FATAL -2 +# define OSSL_RECORD_RETURN_EOF -3 /* * Template for creating a record. A record consists of the |type| of data it - * will contain (e.g. alert, handshake, application data, etc) along with an - * array of buffers in |bufs| of size |numbufs|. There is a corresponding array - * of buffer lengths in |buflens|. Concatenating all of the buffer data together - * would give you the complete plaintext payload to be sent in a single record. + * will contain (e.g. alert, handshake, application data, etc) along with a + * buffer of payload data in |buf| of length |buflen|. */ struct ossl_record_template_st { int type; - void **bufs; - size_t *buflens; - size_t numbufs; + unsigned int version; + const unsigned char *buf; + size_t buflen; }; typedef struct ossl_record_template_st OSSL_RECORD_TEMPLATE; @@ -350,64 +383,86 @@ struct ossl_record_method_st { * force at any one time (one for reading and one for writing). In some * protocols more than 2 might be used (e.g. in DTLS for retransmitting * messages from an earlier epoch). + * + * The created OSSL_RECORD_LAYER object is stored in *ret on success (or + * NULL otherwise). The return value will be one of + * OSSL_RECORD_RETURN_SUCCESS, OSSL_RECORD_RETURN_FATAL or + * OSSL_RECORD_RETURN_NON_FATAL. A non-fatal return means that creation of + * the record layer has failed because it is unsuitable, but an alternative + * record layer can be tried instead. */ /* - * TODO: Will have to be something other than SSL_CIPHER if we make this - * fetchable + * If we eventually make this fetchable then we will need to use something + * other than EVP_CIPHER. Also mactype would not be a NID, but a string. For + * now though, this works. */ - OSSL_RECORD_LAYER *new(int vers, int role, int direction, int level, - unsigned char *secret, size_t secretlen, - SSL_CIPHER *c, BIO *transport, BIO_ADDR *local, - BIO_ADDR *peer, OSSL_PARAM *settings, - OSSL_PARAM *options); - void free(OSSL_RECORD_LAYER *rl); - - int reset(OSSL_RECORD_LAYER *rl); /* Is this needed? */ + int (*new_record_layer)(OSSL_LIB_CTX *libctx, + const char *propq, int vers, + int role, int direction, + int level, + uint16_t epoch, + unsigned char *key, + size_t keylen, + unsigned char *iv, + size_t ivlen, + unsigned char *mackey, + size_t mackeylen, + const EVP_CIPHER *ciph, + size_t taglen, + int mactype, + const EVP_MD *md, + COMP_METHOD *comp, + BIO *prev, + BIO *transport, + BIO *next, + BIO_ADDR *local, + BIO_ADDR *peer, + const OSSL_PARAM *settings, + const OSSL_PARAM *options, + const OSSL_DISPATCH *fns, + void *cbarg, + OSSL_RECORD_LAYER **ret); + int (*free)(OSSL_RECORD_LAYER *rl); + + int (*reset)(OSSL_RECORD_LAYER *rl); /* Is this needed? */ /* Returns 1 if we have unprocessed data buffered or 0 otherwise */ - int unprocessed_read_pending(OSSL_RECORD_LAYER *rl); + int (*unprocessed_read_pending)(OSSL_RECORD_LAYER *rl); + /* * Returns 1 if we have processed data buffered that can be read or 0 otherwise * - not necessarily app data */ - int processed_read_pending(OSSL_RECORD_LAYER *rl); + int (*processed_read_pending)(OSSL_RECORD_LAYER *rl); /* * The amount of processed app data that is internally bufferred and * available to read */ - size_t app_data_pending(OSSL_RECORD_LAYER *rl); - - int write_pending(OSSL_RECORD_LAYER *rl); - - - /* - * Find out the maximum amount of plaintext data that the record layer is - * prepared to write in a single record. When calling write_records it is - * the caller's responsibility to ensure that no record template exceeds - * this maximum when calling write_records. - */ - size_t get_max_record_len(OSSL_RECORD_LAYER *rl); + size_t (*app_data_pending)(OSSL_RECORD_LAYER *rl); /* * Find out the maximum number of records that the record layer is prepared * to process in a single call to write_records. It is the caller's * responsibility to ensure that no call to write_records exceeds this - * number of records. + * number of records. |type| is the type of the records that the caller + * wants to write, and |len| is the total amount of data that it wants + * to send. |maxfrag| is the maximum allowed fragment size based on user + * configuration, or TLS parameter negotiation. |*preffrag| contains on + * entry the default fragment size that will actually be used based on user + * configuration. This will always be less than or equal to |maxfrag|. On + * exit the record layer may update this to an alternative fragment size to + * be used. This must always be less than or equal to |maxfrag|. */ - size_t get_max_records(OSSL_RECORD_LAYER *rl); + size_t (*get_max_records)(OSSL_RECORD_LAYER *rl, int type, size_t len, + size_t maxfrag, size_t *preffrag); /* * Write |numtempl| records from the array of record templates pointed to * by |templates|. Each record should be no longer than the value returned * by get_max_record_len(), and there should be no more records than the * value returned by get_max_records(). - * |allowance| is the maximum amount of "on-the-wire" data that is allowed - * to be sent at the moment (including all QUIC headers, but excluding any - * UDP/IP headers). After a successful or retry return |*sent| will - * be updated with the amount of data that has been sent so far. In the case - * of a retry this could be 0. * Where possible the caller will attempt to ensure that all records are the * same length, except the last record. This may not always be possible so * the record method implementation should not rely on this being the case. @@ -423,24 +478,19 @@ struct ossl_record_method_st { * 0 on retry * -1 on failure */ - int write_records(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE **templates, - size_t numtempl, size_t allowance, size_t *sent); + int (*write_records)(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE *templates, + size_t numtempl); /* * Retry a previous call to write_records. The caller should continue to * call this until the function returns with success or failure. After - * each retry more of the data may have been incrementally sent. |allowance| - * is the amount of "on-the-wire" data that is allowed to be sent at the - * moment. After a successful or retry return |*sent| will - * be updated with the amount of data that has been sent by this call to - * retry_write_records(). + * each retry more of the data may have been incrementally sent. * Returns: * 1 on success * 0 on retry * -1 on failure */ - int retry_write_records(OSSL_RECORD_LAYER *rl, size_t allowance, - size_t *sent); + int (*retry_write_records)(OSSL_RECORD_LAYER *rl); /* * Read a record and return the record layer version and record type in @@ -456,15 +506,99 @@ struct ossl_record_method_st { * Internally the the OSSL_RECORD_METHOD the implementation may read/process * multiple records in one go and buffer them. */ - int read_record(OSSL_RECORD_LAYER *rl, void **rechandle, int *rversion, - int *type, unsigned char **data, size_t *datalen, - uint16_t *epoch, unsigned char *seq_num); + int (*read_record)(OSSL_RECORD_LAYER *rl, void **rechandle, int *rversion, + int *type, unsigned char **data, size_t *datalen, + uint16_t *epoch, unsigned char *seq_num); /* * Release a buffer associated with a record previously read with * read_record. Records are guaranteed to be released in the order that they * are read. */ - void release_record(OSSL_RECORD_LAYER *rl, void *rechandle); + int (*release_record)(OSSL_RECORD_LAYER *rl, void *rechandle); + + /* + * In the event that a fatal error is returned from the functions above then + * get_alert_code() can be called to obtain a more details identifier for + * the error. In (D)TLS this is the alert description code. + */ + int (*get_alert_code)(OSSL_RECORD_LAYER *rl); + + /* + * Update the transport BIO from the one originally set in the + * new_record_layer call + */ + int (*set1_bio)(OSSL_RECORD_LAYER *rl, BIO *bio); + + /* Called when protocol negotiation selects a protocol version to use */ + int (*set_protocol_version)(OSSL_RECORD_LAYER *rl, int version); + + /* + * Whether we are allowed to receive unencrypted alerts, even if we might + * otherwise expect encrypted records. Ignored by protocol versions where + * this isn't relevant + */ + void (*set_plain_alerts)(OSSL_RECORD_LAYER *rl, int allow); + /* + * Called immediately after creation of the record layer if we are in a + * first handshake. Also called at the end of the first handshake + */ + void (*set_first_handshake)(OSSL_RECORD_LAYER *rl, int first); + + /* + * Set the maximum number of pipelines that the record layer should process. + * The default is 1. + */ + void (*set_max_pipelines)(OSSL_RECORD_LAYER *rl, size_t max_pipelines); + + /* + * Called to tell the record layer whether we are currently "in init" or + * not. Default at creation of the record layer is "yes". + */ + void (*set_in_init)(OSSL_RECORD_LAYER *rl, int in_init); + + /* + * Get a short or long human readable description of the record layer state + */ + void (*get_state)(OSSL_RECORD_LAYER *rl, const char **shortstr, + const char **longstr); + + /* + * Set new options or modify ones that were originaly specified in the + * new_record_layer call. + */ + int (*set_options)(OSSL_RECORD_LAYER *rl, const OSSL_PARAM *options); + + const COMP_METHOD *(*get_compression)(OSSL_RECORD_LAYER *rl); + + /* + * Set the maximum fragment length to be used for the record layer. This + * will override any previous value supplied for the "max_frag_len" + * setting during construction of the record layer. + */ + void (*set_max_frag_len)(OSSL_RECORD_LAYER *rl, size_t max_frag_len); + + /* + * The maximum expansion in bytes that the record layer might add while + * writing a record + */ + size_t (*get_max_record_overhead)(OSSL_RECORD_LAYER *rl); + + /* + * Increment the record sequence number + */ + int (*increment_sequence_ctr)(OSSL_RECORD_LAYER *rl); + + /* + * Allocate read or write buffers. Does nothing if already allocated. + * Assumes default buffer length and 1 pipeline. + */ + int (*alloc_buffers)(OSSL_RECORD_LAYER *rl); + + /* + * Free read or write buffers. Fails if there is pending read or write + * data. Buffers are automatically reallocated on next read/write. + */ + int (*free_buffers)(OSSL_RECORD_LAYER *rl); }; ````