Home | History | Annotate | Line # | Download | only in designs
      1 Functions for explicitly fetched PKEY algorithms
      2 ================================================
      3 
      4 Quick background
      5 ----------------
      6 
      7 There are several proposed designs that end up revolving around the same
      8 basic need, explicitly fetched signature algorithms.  The following method
      9 type is affected by this document:
     10 
     11 - `EVP_SIGNATURE`
     12 
     13 Public API - Add variants of `EVP_PKEY_CTX` functionality
     14 ---------------------------------------------------------
     15 
     16 Through OTC discussions, it's been determined that the most suitable APIs to
     17 touch are the of `EVP_PKEY_` functions.
     18 Specifically, `EVP_PKEY_sign()`, `EVP_PKEY_verify()`, `EVP_PKEY_verify_recover()`
     19 and related functions.
     20 They can be extended to accept an explicitly fetched algorithm of the right
     21 type, and to be able to incrementally process indefinite length data streams
     22 when the fetched algorithm permits it (for example, RSA-SHA256).
     23 
     24 It must be made clear that the added functionality cannot be used to compose
     25 an algorithm from different parts.  For example, it's not possible to specify
     26 a `EVP_SIGNATURE` "RSA" and combine it with a parameter that specifies the
     27 hash "SHA256" to get the "RSA-SHA256" functionality.  For an `EVP_SIGNATURE`
     28 "RSA", the input is still expected to be a digest, or some other input that's
     29 limited to the modulus size of the RSA pkey.
     30 
     31 ### Making things less confusing with distinct function names
     32 
     33 Until now, `EVP_PKEY_sign()` and friends were only expected to act on the
     34 pre-computed digest of a message (under the condition that proper flags
     35 and signature md are specified using functions like
     36 `EVP_PKEY_CTX_set_rsa_padding()` and `EVP_PKEY_CTX_set_signature_md()`),
     37 or to act as "primitive" [^1] functions (under the condition that proper
     38 flags are specified, like `RSA_NO_PADDING` for RSA signatures).
     39 
     40 This design proposes an extension to also allow full (not pre-hashed)
     41 messages to be passed, in a streaming style through an *update* and a
     42 *final* function.
     43 
     44 Discussions have revealed that it is potentially confusing to conflate the
     45 current functionality with streaming style functionality into the same name,
     46 so this design separates those out with specific init / update / final
     47 functions for that purpose.  For oneshot functionality, `EVP_PKEY_sign()`
     48 and `EVP_PKEY_verify()` remain supported.
     49 
     50 [^1]: the term "primitive" is borrowed from [PKCS#1](https://www.rfc-editor.org/rfc/rfc8017#section-5)
     51 
     52 ### Making it possible to verify with an early signature
     53 
     54 Some more recent verification algorithms need to obtain the signature
     55 before processing the data.
     56 This is particularly important for streaming modes of operation.
     57 This design proposes a mechanism to accommodate these algorithms
     58 and modes of operation.
     59 
     60 New public API - API Reference
     61 ------------------------------
     62 
     63 ### For limited input size / oneshot signing with `EVP_SIGNATURE`
     64 
     65 ``` C
     66 int EVP_PKEY_sign_init_ex2(EVP_PKEY_CTX *pctx,
     67                            EVP_SIGNATURE *algo,
     68                            const OSSL_PARAM params[]);
     69 ```
     70 
     71 ### For signing a stream with `EVP_SIGNATURE`
     72 
     73 ``` C
     74 int EVP_PKEY_sign_message_init(EVP_PKEY_CTX *pctx,
     75                                EVP_SIGNATURE *algo,
     76                                const OSSL_PARAM params[]);
     77 int EVP_PKEY_sign_message_update(EVP_PKEY_CTX *ctx,
     78                                  const unsigned char *in,
     79                                  size_t inlen);
     80 int EVP_PKEY_sign_message_final(EVP_PKEY_CTX *ctx,
     81                                 unsigned char *sig,
     82                                 size_t *siglen);
     83 #define EVP_PKEY_sign_message(ctx,sig,siglen,tbs,tbslen) \
     84     EVP_PKEY_sign(ctx,sig,siglen,tbs,tbslen)
     85 ```
     86 
     87 ### For limited input size / oneshot verification with `EVP_SIGNATURE`
     88 
     89 ``` C
     90 int EVP_PKEY_verify_init_ex2(EVP_PKEY_CTX *pctx,
     91                              EVP_SIGNATURE *algo,
     92                              const OSSL_PARAM params[]);
     93 ```
     94 
     95 ### For verifying a stream with `EVP_SIGNATURE`
     96 
     97 ``` C
     98 /* Initializers */
     99 int EVP_PKEY_verify_message_init(EVP_PKEY_CTX *pctx,
    100                                  EVP_SIGNATURE *algo,
    101                                  const OSSL_PARAM params[]);
    102 /* Signature setter */
    103 int EVP_PKEY_CTX_set_signature(EVP_PKEY_CTX *pctx,
    104                                unsigned char *sig, size_t siglen,
    105                                size_t sigsize);
    106 /* Update and final */
    107 int EVP_PKEY_verify_message_update(EVP_PKEY_CTX *ctx,
    108                                    const unsigned char *in,
    109                                    size_t inlen);
    110 int EVP_PKEY_verify_message_final(EVP_PKEY_CTX *ctx);
    111 
    112 #define EVP_PKEY_verify_message(ctx,sig,siglen,tbs,tbslen) \
    113     EVP_PKEY_verify(ctx,sig,siglen,tbs,tbslen)
    114 ```
    115 
    116 ### For verify_recover with `EVP_SIGNATURE`
    117 
    118 Preliminary feedback suggests that a streaming interface is uninteresting for
    119 verify_recover, so we only specify a new init function.
    120 
    121 ``` C
    122 /* Initializers */
    123 int EVP_PKEY_verify_recover_init_ex2(EVP_PKEY_CTX *pctx,
    124                                      EVP_SIGNATURE *algo,
    125                                      const OSSL_PARAM params[]);
    126 ```
    127 
    128 Requirements on the providers
    129 -----------------------------
    130 
    131 Because it's not immediately obvious from a composite algorithm name what
    132 key type ("RSA", "EC", ...) it requires / supports, at least in code, allowing
    133 the use of an explicitly fetched implementation of a composite algorithm
    134 requires that providers cooperate by declaring what key type is required /
    135 supported by each algorithm.
    136 
    137 For non-composite operation algorithms (like "RSA"), this is not necessary,
    138 see the fallback strategies below.
    139 
    140 This is to be implemented through an added provider function that would work
    141 like keymgmt's `query_operation_name` function, but would return a NULL
    142 terminated array of key type name instead:
    143 
    144 ``` C
    145 # define OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPE         26
    146 OSSL_CORE_MAKE_FUNC(const char **, signature_query_key_type, (void))
    147 ```
    148 
    149 Furthermore, the distinction of intent, i.e. whether the input is expected
    150 to be a pre-hashed digest or the original message, must be passed on to the
    151 provider.  Because we already distinguish that with function names in the
    152 public API, we use the same mapping in the provider interface.
    153 
    154 The already existing `signature_sign` and `signature_verify` remain as they
    155 are, and can be combined with message init calls.
    156 
    157 ``` C
    158 # define OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT      27
    159 # define OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_UPDATE    28
    160 # define OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL     29
    161 OSSL_CORE_MAKE_FUNC(int, signature_sign_message_init,
    162                     (void *ctx, void *provkey, const OSSL_PARAM params[]))
    163 OSSL_CORE_MAKE_FUNC(int, signature_sign_message_update,
    164                     (void *ctx, const unsigned char *in, size_t inlen))
    165 OSSL_CORE_MAKE_FUNC(int, signature_sign_message_final,
    166                     (void *ctx, unsigned char *sig, size_t *siglen, size_t sigsize))
    167 
    168 # define OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT    30
    169 # define OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_UPDATE  31
    170 # define OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_FINAL   32
    171 OSSL_CORE_MAKE_FUNC(int, signature_verify_message_init,
    172                     (void *ctx, void *provkey, const OSSL_PARAM params[]))
    173 OSSL_CORE_MAKE_FUNC(int, signature_verify_message_update,
    174                     (void *ctx, const unsigned char *in, size_t inlen))
    175 /*
    176  * signature_verify_message_final requires that the signature to be verified
    177  * against is specified via an OSSL_PARAM.
    178  */
    179 OSSL_CORE_MAKE_FUNC(int, signature_verify_message_final, (void *ctx))
    180 ```
    181 
    182 Fallback strategies
    183 -------------------
    184 
    185 Because existing providers haven't been updated to respond to the key type
    186 query, some fallback strategies will be needed for the init calls that take
    187 an explicitly fetched `EVP_SIGNATURE` argument (they can at least be used
    188 for pre-hashed digest operations).  To find out if the `EVP_PKEY` key type
    189 is possible to use with the explicitly fetched algorithm, the following
    190 fallback strategies may be used.
    191 
    192 -   Check if the fetched operation name matches the key type (keymgmt name)
    193     of the `EVP_PKEY` that's involved in the operation.  For example, this
    194     is useful when someone fetched the `EVP_SIGNATURE` "RSA".  This requires
    195     very little modification, as this is already done with the initializer
    196     functions that fetch the algorithm implicitly.
    197 -   Check if the fetched algorithm name matches the name returned by the
    198     keymgmt's `query_operation_name` function.  For example, this is useful
    199     when someone fetched the `EVP_SIGNATURE` "ECDSA", for which the key type
    200     to use is "EC".  This requires very little modification, as this is
    201     already done with the initializer functions that fetch the algorithm
    202     implicitly.
    203 
    204 If none of these strategies work out, the operation initialization should
    205 fail.
    206