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