• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# RFC8152 COSE apis
2
3|||
4|---|---|---|
5|cmake| `LWS_WITH_COSE`|
6|Header| ./include/libwebsockets/lws-cose.h|
7|api-test| ./minimal-examples/api-tests/api-test-cose/|
8|README| ./READMEs/README.cbor-cose.md
9
10COSE is the CBOR equivalent of the JOSE suite of crypto objects and operations.
11You can represent public and private EC, RSA and SYMMETRIC keys, and sets of
12keys of various types; import the logical keys to and from CBOR; and sign /
13verify and encrypt / decrypt payloads using structured CBOR.  Key generation is
14also supported.
15
16|type|operations|algs|
17|---|---|---|
18|lws_cose_key_t|import, export, generation|EC / RSA / SYMMETRIC|
19|cose_sign1|sign, validate|ES256/384/512, RS256/384/512|
20|cose_sign|sign, validate|ES256/384/512, RS256/384/512|
21|cose_mac0|sign, validate|HS256/HS256_64/384/512|
22|cose_mac|validate only|HS256/HS256_64/384/512|
23
24The lws COSE support uses the lws gencrypto layer, which calls through to the
25tls crypto library, and so works on both OpenSSL and mbedTLS the same.
26
27An increasing number of higher-level IETF specifications use COSE underneath.
28
29## cose_key and sets
30
31Lws provides an `lws_cose_key_t` object to contain a single key's metadata and
32key material for EC, RSA and SYMMETRIC key types.
33
34There is a commandline tool wrapping the key dumping and generation apis
35available at `./minimal-examples/crypto/lws-crypto-cose-key`
36
37### cose_key and sets import from CBOR and destroying
38
39```
40lws_cose_key_t *
41lws_cose_key_import(lws_dll2_owner_t *pkey_set, lws_cose_key_import_callback cb,
42		    void *user, const uint8_t *in, size_t len);
43void
44lws_cose_key_destroy(lws_cose_key_t **ck);
45
46void
47lws_cose_key_set_destroy(lws_dll2_owner_t *o);
48```
49
50To convert a single key, `pkey_set` should be NULL and the created key will be
51returned, for a cose_key set, which is simply a CBOR array of cose_keys, it
52should be a prepared (ie, zero'd down if nothing in it) lws_dll2_owner_t that
53will contain the resulting list of `lws_cose_key_t` objects that were created.
54In both cases the return is NULL if there was a fatal error and anything created
55has been cleaned up, the return has no other meaning in the cose_key set case.
56
57`lws_cose_key_destroy()` destroys a single `lws_cose_key_t` and sets the
58contents of the pointer to NULL, for cose_key sets you instead pass a pointer to
59the owner object to `lws_cose_key_set_destroy()` to destroy all the keys in the
60set in one step.
61
62cose_key has some confusions about type, kty and alg may be either ints,
63representing well-known standardized key and alg types, or freeform strings.
64We convert the well-known ints to their string representations at import, so
65there can be no confusion later.
66
67### cose_key generation
68
69```
70lws_cose_key_t *
71lws_cose_key_generate(struct lws_context *context, int cose_kty, int use_mask,
72		       int bits, const char *curve, const char *kid);
73```
74
75This creates an `lws_cose_key_t`, generates a key (SYMMETRIC) or keypair into
76it and returns a pointer to it.
77
78`cose_kty` is one of `LWSCOSE_WKKTV_OKP`, `LWSCOSE_WKKTV_EC2`, `LWSCOSE_WKKTV_RSA`,
79or `LWSCOSE_WKKTV_SYMMETRIC`.  `bits` is valid for RSA keys and for EC keys,
80`curve` should be a well-known curve name, one of `P-256`, `P-384` and `P-521`
81currently.  `use_mask` is a bitfield made up of  (1 << LWSCOSE_WKKO_...) set to
82enable the usage on the key.
83
84### cose_key export to CBOR
85
86The export api uses the same CBOR write context as `lws_lec_printf()` uses to
87emit the key into an output buffer.  Like the CBOR output apis, it may return
88`LWS_LECPCTX_RET_AGAIN` to indicate it filled the buffer and should be called
89again to fill another buffer.  `lws_lec_init()` should be used to prepare the
90write context and `lws_lec_setbuf()` to reset the output buffer on subsequent
91calls, exactly the same as the CBOR write apis.
92
93```
94enum lws_lec_pctx_ret
95lws_cose_key_export(lws_cose_key_t *ck, lws_lec_pctx_t *ctx, int flags);
96```
97
98`flags` may be 0 to only output the public key pieces, or `LWSJWKF_EXPORT_PRIVATE`
99to output everything.
100
101## Signing and signature validation
102
103COSE specifies three kinds of signed object, `cose_sign1` which signs a payload
104with a single algorithm and key, `cose_sign` which may sign a payload with
105multiple algorithms and keys, and `countersign`.
106
107`cose_sign1` has the advantage it can be validated with a single pass through
108the signed object; `cose_sign` unfortunately specifies the parameters of the
109signatures after the payload and must be done with multiple passes through the
110payload, for inline payloads, by caching it in heap.
111
112`cose_sign` and `cose_sign1` objects are supported by lws, Countersigned
113objects are not yet supported.
114
115`cose_mac0` is supported using HMAC for signing and validation, `cose_mac` is
116only supported for validation.
117
118There is a commandline tool wrapping the signing and validation apis
119available at `./minimal-examples/crypto/lws-crypto-cose-sign`
120
121### Signature validation
122
123Signature validation does not have to be done synchronously, to facilitate this
124first you create a validation context specifying the type (eg, `SIGTYPE_SINGLE`)
125and a keyset of public keys the signature might use to validate (notice even a
126single key is passed in an lws_dll2_owner_t keyset).
127
128Creation uses a public `lws_cose_validate_create_info_t` info struct
129
130```
131typedef struct lws_cose_validate_create_info {
132	struct lws_context		*cx;
133	/**< REQUIRED: the lws context */
134	lws_dll2_owner_t		*keyset;
135	/**< REQUIRED: one or more cose_keys */
136
137	enum lws_cose_sig_types		sigtype;
138	/**<  0 if a CBOR tag is in the sig, else one of SIGTYPE_MULTI,
139	 * SIGTYPE_SINGLE, etc*/
140
141	lws_cose_validate_pay_cb_t	pay_cb;
142	/**< optional: called back with unvalidated payload pieces */
143	void				*pay_opaque;
144	/**< optional: passed into pay_cb callback along with payload chunk */
145
146	lws_cose_sign_ext_pay_cb_t	ext_cb;
147	/**< optional extra application data provision callback */
148	void				*ext_opaque;
149	/**< optional extra application data provision callback opaque */
150	size_t				ext_len;
151	/**< if we have extra app data, this must be set to the length of it */
152} lws_cose_validate_create_info_t;
153```
154
155```
156struct lws_cose_validate_context *
157lws_cose_validate_create(const lws_cose_validate_create_info_t *info);
158
159void
160lws_cose_validate_destroy(struct lws_cose_validate_context **cps);
161```
162
163after that as pieces of the signature CBOR become available, they can be
164processed by the validation context
165
166```
167int
168lws_cose_validate_chunk(struct lws_cose_validate_context *cps,
169			const uint8_t *in, size_t in_len, size_t *used_in);
170```
171
172The parsing of the signature yields a list of result objects indicating
173information about each signature it encountered and whether it was validated or
174not.  The parsing itself only fails if there is an unrecoverable error, the
175completion of parsing does not indicate validation, it may yield zero or more
176result objects indicating the validation failed.
177
178```
179lws_dll2_owner_t *
180lws_cose_validate_results(struct lws_cose_validate_context *cps);
181
182typedef struct {
183	lws_dll2_t		list;
184
185	const lws_cose_key_t	*cose_key;
186	cose_param_t		cose_alg;
187
188	int			result; /* 0 = validated */
189
190} lws_cose_validate_res_t;
191```
192
193It's like this because for multiple signatures, we may only have keys for some
194of them, and we may have different policies for validation that can only be
195assessed as a whole, eg, we may inisit that signatures pass with specific
196algorithms, or all signatures for specific keys must be present and pass.  This
197way user code can assess the situation after the signature parsing and make its
198decision about overall validity according to its own policies.
199
200## Signing
201
202Signing is again done by creating a signing context using an info struct to pass
203in the paramter (a `lws_cose_sign_create_info_t`).
204
205```
206#define LCSC_FL_ADD_CBOR_TAG		(1 << 0)
207#define LCSC_FL_ADD_CBOR_PREFER_MAC0	(1 << 1)
208
209typedef struct lws_cose_sign_create_info {
210	struct lws_context		*cx;
211	/**< REQUIRED: the lws context */
212	lws_dll2_owner_t		*keyset;
213	/**< REQUIRED: one or more cose_keys */
214
215	lws_lec_pctx_t			*lec;
216	/**< REQUIRED: the cbor output context to emit to, user must
217	 * initialize with lws_lec_init() beforehand */
218
219	lws_cose_sign_ext_pay_cb_t	ext_cb;
220	/**< optional extra application data provision callback */
221	void				*ext_opaque;
222	/**< optional extra application data provision callback opaque */
223	size_t				ext_len;
224	/**< if we have extra app data, this must be set to the length of it */
225
226	size_t				inline_payload_len;
227	/**< REQUIRED: size of the inline payload we will provide */
228
229	int				flags;
230	/**< bitmap of  LCSC_FL_* */
231	enum lws_cose_sig_types		sigtype;
232	/**< 0, or sign type hint */
233} lws_cose_sign_create_info_t;
234```
235
236```
237struct lws_cose_sign_context *
238lws_cose_sign_create(const lws_cose_sign_create_info_t *info);
239```
240
241After creating the signing context, you call `lws_cose_sign_add()` one or more
242times to add algorithms and keys to sign with (since cose_sign allows multiple
243recipients with the same payload signed in different ways).
244
245```
246int
247lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg,
248		  const lws_cose_key_t *ck);
249```
250
251The payload does not have to be provided all at once and can be passed in chunk
252by chunk over time via `lws_cose_sign_payload_chunk()`.
253
254Output is mediated via an lws CBOR output context provided in the info at
255creation-time, it's only emitted during the `lws_cose_sign_payload_chunk()`
256phase.  If it returns `LWS_LECPCTX_RET_AGAIN`, you must call that api again
257after using the CBOR output context data and resetting its buffer by
258`lws_lec_setbuf()`, so it can continue to output.
259
260```
261enum lws_lec_pctx_ret
262lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc,
263			    const uint8_t *in, size_t in_len);
264```
265
266Finally the signing context is destroyed.
267
268```
269void
270lws_cose_sign_destroy(struct lws_cose_sign_context **csc);
271```
272
273