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