1EVP APIs for supporting cipher pipelining in provided ciphers
2=============================================================
3
4OpenSSL previously supported "pipeline" ciphers via ENGINE implementations.
5That support was lost when we moved to providers. This document discusses API
6design to restore that capability and enable providers to implement such
7ciphers.
8
9Pipeline operation
10-------------------
11
12Certain ciphers, such as AES-GCM, can be optimized by computing blocks in
13parallel. Cipher pipelining support allows application to submit multiple
14chunks of data in one cipher update call, thereby allowing the provided
15implementation to take advantage of parallel computing. This is very beneficial
16for hardware accelerators as pipeline amortizes the latency over multiple
17chunks. Our libssl makes use of pipeline as discussed in
18[here](https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_max_pipelines.html).
19
20Pipelining with ENGINE
21-----------------------
22
23Before discussing API design for providers, let's take a look at existing
24pipeline API that works with engines.
25
26**EVP Interface:**
27Flag to denote pipeline support
28
29```c
30cipher->flags & EVP_CIPH_FLAG_PIPELINE
31```
32
33Input/output and aad buffers are set using `EVP_CIPHER_CTX_ctrl()`
34
35```c
36EVP_CIPHER_CTX_ctrl()
37    - EVP_CTRL_AEAD_TLS1_AAD (loop: one aad at a time)
38    - EVP_CTRL_SET_PIPELINE_OUTPUT_BUFS (array of buffer pointers)
39    - EVP_CTRL_SET_PIPELINE_INPUT_BUFS (array of buffer pointers)
40    - EVP_CTRL_SET_PIPELINE_INPUT_LENS
41```
42
43Single-call cipher invoked to perform encryption/decryption.
44
45```c
46EVP_Cipher()
47```
48
49Proposal for EVP pipeline APIs
50-------------------------------------
51
52Current API design is made similar to non-pipeline counterpart. The document
53will be final once the changes are integrated.
54
55**EVP Interface:**
56API to check for pipeline support in provided cipher.
57
58```c
59/**
60 * @brief checks if the provider has exported required pipeline functions
61 * This function works only with explicitly fetched EVP_CIPHER instances. i.e.
62 * fetched using `EVP_CIPHER_fetch()`. For non-fetched ciphers, it returns 0.
63 *
64 * @param enc   1 for encryption, 0 for decryption
65 * @return 0 (pipeline not supported) or 1 (pipeline supported)
66 */
67int EVP_CIPHER_can_pipeline(const EVP_CIPHER *cipher, int enc);
68```
69
70Multi-call APIs for init, update and final. Associated data for AEAD ciphers
71are set in `EVP_CipherPipelineUpdate`.
72
73```c
74/**
75 * @param iv    array of pointers (array length must be numpipes)
76 */
77int EVP_CipherPipelineEncryptInit(EVP_CIPHER_CTX *ctx,
78                                  const EVP_CIPHER *cipher,
79                                  const unsigned char *key, size_t keylen,
80                                  size_t numpipes,
81                                  const unsigned char **iv, size_t ivlen);
82int EVP_CipherPipelineDecryptInit(EVP_CIPHER_CTX *ctx,
83                                  const EVP_CIPHER *cipher,
84                                  const unsigned char *key, size_t keylen,
85                                  size_t numpipes,
86                                  const unsigned char **iv, size_t ivlen);
87
88/*
89 * @param out      array of pointers to output buffers (array length must be
90 *                 numpipes)
91 *                 when NULL, input buffers are treated as AAD data
92 * @param outl     pointer to array of output length (array length must be
93 *                 numpipes)
94 * @param outsize  pointer to array of output buffer size (array length must be
95 *                 numpipes)
96 * @param in       array of pointers to input buffers (array length must be
97 *                 numpipes)
98 * @param inl      pointer to array of input length (array length must be numpipes)
99 */
100int EVP_CipherPipelineUpdate(EVP_CIPHER_CTX *ctx,
101                             unsigned char **out, size_t *outl,
102                             const size_t *outsize,
103                             const unsigned char **in, const size_t *inl);
104
105/*
106 * @param outm     array of pointers to output buffers (array length must be
107 *                 numpipes)
108 * @param outl     pointer to array of output length (array length must be
109 *                 numpipes)
110 * @param outsize  pointer to array of output buffer size (array length must be
111 *                 numpipes)
112 */
113int EVP_CipherPipelineFinal(EVP_CIPHER_CTX *ctx,
114                            unsigned char **outm, size_t *outl,
115                            const size_t *outsize);
116```
117
118API to get/set AEAD auth tag.
119
120```c
121/**
122 * @param buf   array of pointers to aead buffers (array length must be
123 *              numpipes)
124 * @param bsize size of one buffer (all buffers must be of same size)
125 *
126 * AEAD tag len is set using OSSL_CIPHER_PARAM_AEAD_TAGLEN
127 */
128OSSL_CIPHER_PARAM_PIPELINE_AEAD_TAG (type OSSL_PARAM_OCTET_PTR)
129```
130
131**Alternative:** iovec style interface for input/output buffers.
132
133```c
134typedef struct {
135    unsigned char *buf;
136    size_t buf_len;
137} EVP_CIPHER_buf;
138
139/**
140 * @param out       array of EVP_CIPHER_buf containing output buffers (array
141 *                  length must be numpipes)
142 *                  when this param is NULL, input buffers are treated as AAD
143 *                  data (individual pointers within array being NULL will be
144 *                  an error)
145 * @param in        array of EVP_CIPHER_buf containing input buffers (array
146 *                  length must be numpipes)
147 * @param stride    The stride argument must be set to sizeof(EVP_CIPHER_buf)
148 */
149EVP_CipherPipelineUpdate(EVP_CIPHER_CTX *ctx, EVP_CIPHER_buf *out,
150                          EVP_CIPHER_buf *in, size_t stride);
151
152/**
153 * @param outm      array of EVP_CIPHER_buf containing output buffers (array
154 *                  length must be numpipes)
155 * @param stride    The stride argument must be set to sizeof(EVP_CIPHER_buf)
156 */
157EVP_CipherPipelineFinal(EVP_CIPHER_CTX *ctx,
158                          EVP_CIPHER_buf *out, size_t stride);
159
160/**
161 * @param buf       array of EVP_CIPHER_buf containing output buffers (array
162 *                  length must be numpipes)
163 * @param bsize     stride; sizeof(EVP_CIPHER_buf)
164 */
165OSSL_CIPHER_PARAM_PIPELINE_AEAD_TAG (type OSSL_PARAM_OCTET_PTR)
166```
167
168**Design Decisions:**
169
1701. Denoting pipeline support
171    - [ ] a. A cipher flag `EVP_CIPH_FLAG_PROVIDED_PIPELINE` (this has to be
172      different than EVP_CIPH_FLAG_PIPELINE, so that it doesn't break legacy
173      applications).
174    - [x] b. A function `EVP_CIPHER_can_pipeline()` that checks if the provider
175      exports pipeline functions.
176    > **Justification:** flags variable is deprecated in EVP_CIPHER struct.
177    > Moreover, EVP can check for presence of pipeline functions, rather than
178    > requiring providers to set a flag.
179
180With the introduction of this new API, there will be two APIs for
181pipelining available until the legacy code is phased out:
182    a. When an Engine that supports pipelining is loaded, it will set the
183      `ctx->flags & EVP_CIPH_FLAG_PIPELINE`. If this flag is set, applications
184      can continue to use the legacy API for pipelining.
185    b. When a Provider that supports pipelining is fetched,
186      `EVP_CIPHER_can_pipeline()` will return true, allowing applications to
187      utilize the new API for pipelining.
188
1892. `numpipes` argument
190    - [x] a. `numpipes` received only in `EVP_CipherPipelineEncryptInit()` and
191      saved in EVP_CIPHER_CTX for further use.
192    - [ ] b. `numpipes` value is repeatedly received in each
193      `EVP_CipherPipelineEncryptInit()`, `EVP_CipherPipelineUpdate()` and
194      `EVP_CipherPipelineFinal()` call.
195    > **Justification:** It is expected for numpipes to be same across init,
196    > update and final operation.
197
1983. Input/Output buffers
199    - [x] a. A set of buffers is represented by an array of buffer pointers and
200      an array of lengths. Example: `unsigned char **out, size_t *outl`.
201    - [ ] b. iovec style: A new type that holds one buffer pointer along with
202      its size. Example: `EVP_CIPHER_buf *out`
203    > **Justification:** While iovec style is better buffer representation, the
204    > EVP - provider interface in core_dispatch.h uses only primitive types.
205
2064. AEAD tag
207    - [x] a. A new OSSL_CIPHER_PARAM of type OSSL_PARAM_OCTET_PTR,
208      `OSSL_CIPHER_PARAM_PIPELINE_AEAD_TAG`, that uses an array of buffer
209      pointers. This can be used with `iovec_buf` if we decide with 3.b.
210    - [ ] b. Reuse `OSSL_CIPHER_PARAM_AEAD_TAG` by using it in a loop,
211      processing one tag at a time.
212    > **Justification:** Reduces cipher get/set param operations.
213
214Future Ideas
215------------
216
2171. It would be nice to have a mechanism for fetching provider with pipeline
218   support over other providers that don't support pipeline. Maybe by using
219   property query strings.
220