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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/** @file */
/** @ingroup oscore_native_api
* @addtogroup oscore_native_crypto Native cryptography API
*
* @brief API which any native cryotography library provides to OSCORE
*
* The below functions are tailored towards cryptography libraries that work
* in terms of COSE algorithms, because that's what OSCORE uses by specification.
* Cryptography libraries that do not "think" that way will likely implement
* most of the functions by a large switch statement that dispatches
* encryption and decryption into the appropriate (eg. AES-CCM) functions.
*
* Some aspects of these calls are tailored towards AEAD with streaming AAD.
* While there is no practical need for having the plain- and ciphertext
* processed as they come in (because acting on them would have severe
* security ramifications), AAD is unbounded when Class-I options are present,
* and not otherwise needed present in a contiguous buffer.
*
* Many backends (even the currently used libCOSE, though there are efforts to
* change that) will not support that mode of operation. Those need to either
* allocate memory dynamically at the start of the AEAD operation (based on
* the known size of the AAD that is passed in), or set aside that memory in
* their @ref oscore_crypto_aead_encryptstate_t. (The memory size is a
* nonlinear function of the maximum key lengths and algorithms; 32 byte will
* often suffice as long as no Class-I options are present).
*
* @{
*/
/** @brief Set up an algorithm descriptor from a numerically identified COSE
* Algorithm
*
* @param[out] alg Output field for the algorithm
* @param[in] number Algorithm number from IANA's COSE Algorithms registry
*
* If the number given is not an AEAD algorithm or not implemented, the
* function must return an error; it may leave @p alg uninitialized or set it
* arbitrarily in that case.
*
*/
oscore_cryptoerr_t ;
/** @brief Set up an algorithm descriptor from a string-identified COSE
* Algorithm
*
* @param[out] alg Output field for the algorithm
* @param[in] string Algorithm name from IANA's COSE Algorithms registry, in
* UTF-8 encoding
* @param[in] string_len Length of @p string
*
* If the number given is not an AEAD algorithm or not implemented, the
* function must return an error; it may leave @p alg uninitialized or set it
* arbitrarily in that case.
*
*/
oscore_cryptoerr_t ;
/** @brief Obtain the algorithms's numeric COSE identifier
*
* @param[in] alg Algorithm
* @param[out] number Memory address to be populated with the COSE identifier
*
* Returns an OK value if a numeric identifier exists for the algorithm; if
* not, a string identifier needs to exist.
*/
oscore_cryptoerr_t ;
/** @brief Obtain the algorithms's string COSE identifier
*
* @param[in] alg Algorithm
* @param[out] string Memory address to be populated with the location of a COSE identifier
* @param[out] string_len Memory address to be populated with the COSE identifier's length
*
* The memory location containing the string is expected to be static and constant.
*
* Returns an OK value if a string identifier exists for the algorithm; if
* not, a numeric identifier needs to exist.
*/
oscore_cryptoerr_t ;
/** @brief Get the tag length that is used for a particular algorithm
*
* @param[in] alg An AEAD algorithm
* @return the length of the algorithm's tag, in bytes
*
* Implementers note: This function is easy enough to be infallible. As the
* only way to obtain the backend's @ref oscore_crypto_aeadalg_t is through
* @ref oscore_crypto_aead_from_number and @ref oscore_crypto_aead_from_string,
* it can be sure that only values that were produced by that are around. If a
* backend can still wind up in a situation where it doesn't know the tag
* length, returning SIZE_MAX is a safe way to ensure that rather than
* out-of-buffer writes, deterministic failure occurs.
*/
size_t ;
/** @brief Get the IV (initialization vector / nonce) length that is used for a particular algorithm
*
* @param[in] alg An AEAD algorithm
* @return the length of the algorithm's IV, in bytes
*/
size_t ;
/** @brief Get the key length that is used for a particular algorithm
*
* @param[in] alg An AEAD algorithm
* @return the length of the algorithm's keys, in bytes
*/
size_t ;
/** @brief Start an AEAD encryption operation
*
* @param[out] state Encryption state to set up
* @param[in] alg OSCORE AEAD algorithm that will be used
* @param[in] aad_len Bytes of Additional Authenticated Data that will later fed into this encryption
* @param[in] plaintext_len Bytes of plaintext that will later fed into this encryption
* @param[in] iv Nonce used for this encryption (length depends on the algorithm)
* @param[in] key Shared key used for this encryption (length depends on the algorithm)
*
* The construction of this encryption state must be followed up with exactly
* as many bytes in @ref oscore_crypto_aead_encrypt_feed_aad calls, and an @ref
* oscore_crypto_aead_encrypt_inplace call with a buffer of the given size.
*
* There is no dedicated function to clean up the @ref state, that happens in
* any function that ends an encryption operation (which is currently only @ref
* oscore_crypto_aead_encrypt_inplace).
*
* Backends that implement streaming algorithms that do not need to know the
* lengths in advance are free to ignore the provided lengths.
*
* @todo Document possible causes of unsuccessful operation in this and the
* following methods, describe interaction with the state (the erring function
* must do any cleanup, the caller can't keep using it), and think over whether
* those may be doable in an infallible way if the oscore_crypto_aeadalg_t
* construction has succeeded. (There needs to be an out-of-memory or
* AAD-too-long condition for non-stream-AAD backends, but maybe the rest can
* be documented to be infallible? What if the caller messes up lengths?)
*/
oscore_cryptoerr_t ;
/** @brief Provide Additional Authenticated Data (AAD) for an ongoing AEAD encryption operation
*
* @param[inout] state Encryption state to update
* @param[in] aad_chunk Data to be processed as AAD
* @param[in] aad_chunk_len Length of @param aad
*
* This advances the internal @ref state of the encryption by processing the
* AAD. It may be called as many times with various non-zero lengths as the
* caller wants, as long as the total number of bytes fed in is the aad_len
* given in the initial @ref oscore_crypto_aead_encrypt_start call.
*
* Implementations of algorithms that do not process AAD byte-by-byte may need
* to aggregate the AAD data in blocks and keep room for that with the @param
* state. That case is common.
*
* Some backend libraries require the full AAD to be in contiguous memory.
* Those can dynamically allocate memory at encryption start, or set aside a
* limited buffer and refuse to operate on overly large AADs. That case is
* common outside the embedded area where those allocations are affordable;
* high-quality embedded libraries will make do with a block-sized buffer.
*
* Note that the @p state argument is a void pointer. This is necessary to
* handle feeding into encryption and decryption states (which can be different
* in some implementations) in an efficient and conformant way. (To the curious
* reader, yours truly recommends the excellent summary of the situation [by
* Adam Rosenfield](https://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type)
* about the incompatibility of `void*` and `struct*` pointers, and special
* consideration for the comparatively exotic compilers in use for constrained
* devices).
*/
oscore_cryptoerr_t ;
/** @brief Finish an AEAD encryption operation by encrypting a buffer in place and appending the tag
*
* @param[inout] state Encryption state use and finalize
* @param[inout] buffer Memory location in which the plaintext is encrypted and the tag appended
* @param[in] buffer_len Writable size of the buffer
*
* The @param buffer_len must be exactly the sum of the ``plaintext_len`` given
* at setup, and the algorithm's tag length; the backends may rely on that. The
* length is given explicitly to ensure that no writes happen outside the
* provided buffer in case the involved parties disagree on any of the input
* values, and to ease static analysis.
*
* The function reads the plaintext from @param buffer, writes the resulting
* ciphertext to the same location, and writes the AEAD tag right after it to
* the end of the buffer.
*/
oscore_cryptoerr_t ;
/** @brief Start an AEAD decryption operation
*
* This is fully analogous to @ref oscore_crypto_aead_encrypt_start; see there.
*
*/
oscore_cryptoerr_t ;
/** @brief Provide Additional Authenticated Data (AAD) for an ongoing AEAD decryption operation
*
* This is fully analogous to @ref oscore_crypto_aead_decrypt_feed_aad; see there.
*/
oscore_cryptoerr_t ;
/** @brief Finish an AEAD decryption operation by decrypting a buffer that holds ciphertext followed by tag in place
*
* This is largely analogous to @ref oscore_crypto_aead_encrypt_inplace.
*
* @param[inout] state Decryption state use and finalize
* @param[inout] buffer Memory location in which the concatenation of ciphertext and tag is stored, and where the plaintext will be written to
* @param[in] buffer_len Readable size of the buffer (writes will happen to all places but usually not to the last tag bytes)
*
* The @param buffer_len must be exactly the sum of the ``plaintext_len`` given
* at setup, and the algorithm's tag length; the backends may rely on that. The
* length is given explicitly to ensure that no reads or writes happen outside
* the provided buffer in case the involved parties disagree on any of the
* input values, and to ease static analysis.
*
* The function reads the ciphertext and the tag from @param buffer, and writes
* the resulting plaintext to the same location. Implementations usually leave
* the tag bytes in place, but may leave them in any state.
*/
oscore_cryptoerr_t ;
/** @brief Set up an algorithm descriptor from a numerically identified COSE
* Direct Key with KDF
*
* @param[out] alg Output field for the algorithm
* @param[in] number Algorithm number from IANA's COSE Algorithms registry
*
* If the number given is not an HKDF algorithm or not implemented, the
* function must return an error; it may leave @p alg uninitialized or set it
* arbitrarily in that case.
*
* @FIXME There is some inconsistency here in how the number is treated ("is 5
* a valid input, or -10?"), see
* <https://gitlab.com/oscore/liboscore/-/issues/58> for details.
*
*/
oscore_cryptoerr_t ;
/** @brief Set up an algorithm descriptor from a string-identified COSE
* Direct Key with KDF
*
* @param[out] alg Output field for the algorithm
* @param[in] string Algorithm name from IANA's COSE Algorithms registry, in
* UTF-8 encoding
* @param[in] string_len Length of @p string
*
* If the number given is not an HKDF algorithm or not implemented, the
* function must return an error; it may leave @p alg uninitialized or set it
* arbitrarily in that case.
*
*/
oscore_cryptoerr_t ;
/** @brief Run a single HKDF derivation (ie. extraction and expansion)
*
* @param[in] alg HKDF algorithm
* @param[in] salt The Salt fed into the extract step (in the "key" position)
* @param[in] salt_len Length of @salt
* @param[in] ikm The Input Keying Material (IKM) fed into the extract step (in the "input" position)
* @param[in] ikm_len Length of @p ikm
* @param[in] info Application specific informartion fed into the expand steps
* @param[in] info_len Length of @p info
* @param[out] out Buffer into which the expand output is to be placed
* @param[in] out_len Length of @p out
*
* @return a successful cryptoerr value unless @p out_len is so large that the HKDF fails.
*
* Design notes:
*
* Having info as a buffer is really inconvenient as it'd be more convenient to feedd that slice
* by slice given it contains potentially long id / id_context. A future change
* here could remove the need for contiguously allocated @p info (also possibly
* @p salt or @p ikm) data; the challenge will be that @p info is needed
* multiple times if @p out_len exceeds the HKDF algorithm's block size -- in
* theory, at least. (In practice, what gets extracted is a key and an IV, and
* both are typically <= 32 bytes which the common SHA-256 HKDF already
* provides in a single pass).
*
* Running expand and extract independently could save some steps, especially
* during group rekeying.
*/
oscore_cryptoerr_t ;
/** Return true if an error type indicates an unsuccessful operation */
bool ;
/** @} */