2 * libwebsockets - JSON Web Encryption support
4 * Copyright (C) 2018 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * JWE code related to ecdh-es + Concat KDF and aes kw
26 #include "private-lib-core.h"
27 #include "private-lib-jose-jwe.h"
32 * 4.6. Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static
35 * This section defines the specifics of key agreement with Elliptic
36 * Curve Diffie-Hellman Ephemeral Static [RFC6090], in combination with
37 * the Concat KDF, as defined in Section 5.8.1 of [NIST.800-56A]. The
38 * key agreement result can be used in one of two ways:
40 * 1. directly as the Content Encryption Key (CEK) for the "enc"
41 * algorithm, in the Direct Key Agreement mode, or
43 * 2. as a symmetric key used to wrap the CEK with the "A128KW",
44 * "A192KW", or "A256KW" algorithms, in the Key Agreement with Key
47 * A new ephemeral public key value MUST be generated for each key
48 * agreement operation.
50 * In Direct Key Agreement mode, the output of the Concat KDF MUST be a
51 * key of the same length as that used by the "enc" algorithm. In this
52 * case, the empty octet sequence is used as the JWE Encrypted Key
53 * value. The "alg" (algorithm) Header Parameter value "ECDH-ES" is
54 * used in the Direct Key Agreement mode.
56 * In Key Agreement with Key Wrapping mode, the output of the Concat KDF
57 * MUST be a key of the length needed for the specified key wrapping
58 * algorithm. In this case, the JWE Encrypted Key is the CEK wrapped
59 * with the agreed-upon key.
61 * The following "alg" (algorithm) Header Parameter values are used to
62 * indicate that the JWE Encrypted Key is the result of encrypting the
63 * CEK using the result of the key agreement algorithm as the key
64 * encryption key for the corresponding key wrapping algorithm:
66 * +-----------------+-------------------------------------------------+
67 * | "alg" Param | Key Management Algorithm |
69 * +-----------------+-------------------------------------------------+
70 * | ECDH-ES+A128KW | ECDH-ES using Concat KDF and CEK wrapped with |
72 * | ECDH-ES+A192KW | ECDH-ES using Concat KDF and CEK wrapped with |
74 * | ECDH-ES+A256KW | ECDH-ES using Concat KDF and CEK wrapped with |
76 * +-----------------+-------------------------------------------------+
78 * 4.6.1. Header Parameters Used for ECDH Key Agreement
80 * The following Header Parameter names are used for key agreement as
83 * 4.6.1.1. "epk" (Ephemeral Public Key) Header Parameter
85 * The "epk" (ephemeral public key) value created by the originator for
86 * the use in key agreement algorithms. This key is represented as a
87 * JSON Web Key [JWK] public key value. It MUST contain only public key
88 * parameters and SHOULD contain only the minimum JWK parameters
89 * necessary to represent the key; other JWK parameters included can be
90 * checked for consistency and honored, or they can be ignored. This
91 * Header Parameter MUST be present and MUST be understood and processed
92 * by implementations when these algorithms are used.
94 * 4.6.1.2. "apu" (Agreement PartyUInfo) Header Parameter
96 * The "apu" (agreement PartyUInfo) value for key agreement algorithms
97 * using it (such as "ECDH-ES"), represented as a base64url-encoded
98 * string. When used, the PartyUInfo value contains information about
99 * the producer. Use of this Header Parameter is OPTIONAL. This Header
100 * Parameter MUST be understood and processed by implementations when
101 * these algorithms are used.
103 * 4.6.1.3. "apv" (Agreement PartyVInfo) Header Parameter
105 * The "apv" (agreement PartyVInfo) value for key agreement algorithms
106 * using it (such as "ECDH-ES"), represented as a base64url encoded
107 * string. When used, the PartyVInfo value contains information about
108 * the recipient. Use of this Header Parameter is OPTIONAL. This
109 * Header Parameter MUST be understood and processed by implementations
110 * when these algorithms are used.
112 * 4.6.2. Key Derivation for ECDH Key Agreement
114 * The key derivation process derives the agreed-upon key from the
115 * shared secret Z established through the ECDH algorithm, per
116 * Section 6.2.2.2 of [NIST.800-56A].
118 * Key derivation is performed using the Concat KDF, as defined in
119 * Section 5.8.1 of [NIST.800-56A], where the Digest Method is SHA-256.
120 * The Concat KDF parameters are set as follows:
123 * This is set to the representation of the shared secret Z as an
127 * This is set to the number of bits in the desired output key. For
128 * "ECDH-ES", this is length of the key used by the "enc" algorithm.
129 * For "ECDH-ES+A128KW", "ECDH-ES+A192KW", and "ECDH-ES+A256KW", this
130 * is 128, 192, and 256, respectively.
133 * The AlgorithmID value is of the form Datalen || Data, where Data
134 * is a variable-length string of zero or more octets, and Datalen is
135 * a fixed-length, big-endian 32-bit counter that indicates the
136 * length (in octets) of Data. In the Direct Key Agreement case,
137 * Data is set to the octets of the ASCII representation of the "enc"
138 * Header Parameter value. In the Key Agreement with Key Wrapping
139 * case, Data is set to the octets of the ASCII representation of the
140 * "alg" (algorithm) Header Parameter value.
143 * The PartyUInfo value is of the form Datalen || Data, where Data is
144 * a variable-length string of zero or more octets, and Datalen is a
145 * fixed-length, big-endian 32-bit counter that indicates the length
146 * (in octets) of Data. If an "apu" (agreement PartyUInfo) Header
147 * Parameter is present, Data is set to the result of base64url
148 * decoding the "apu" value and Datalen is set to the number of
149 * octets in Data. Otherwise, Datalen is set to 0 and Data is set to
150 * the empty octet sequence.
153 * The PartyVInfo value is of the form Datalen || Data, where Data is
154 * a variable-length string of zero or more octets, and Datalen is a
155 * fixed-length, big-endian 32-bit counter that indicates the length
156 * (in octets) of Data. If an "apv" (agreement PartyVInfo) Header
157 * Parameter is present, Data is set to the result of base64url
158 * decoding the "apv" value and Datalen is set to the number of
159 * octets in Data. Otherwise, Datalen is set to 0 and Data is set to
160 * the empty octet sequence.
163 * This is set to the keydatalen represented as a 32-bit big-endian
167 * This is set to the empty octet sequence.
169 * Applications need to specify how the "apu" and "apv" Header
170 * Parameters are used for that application. The "apu" and "apv" values
171 * MUST be distinct, when used. Applications wishing to conform to
172 * [NIST.800-56A] need to provide values that meet the requirements of
173 * that document, e.g., by using values that identify the producer and
174 * consumer. Alternatively, applications MAY conduct key derivation in
175 * a manner similar to "Diffie-Hellman Key Agreement Method" [RFC2631]:
176 * in that case, the "apu" parameter MAY either be omitted or represent
177 * a random 512-bit value (analogous to PartyAInfo in Ephemeral-Static
178 * mode in RFC 2631) and the "apv" parameter SHOULD NOT be present.
184 * - ECDH-ES[-variant] comes in the jose "alg" and just covers key agreement.
185 * The "enc" action is completely separate and handled elsewhere. However
186 * the key size throughout is determined by the needs of the "enc" action.
188 * - The jwe->jws.jwk is the PEER - the encryption consumer's - public key.
190 * - The public part of the ephemeral key comes out in jose.jwk_ephemeral
192 * - Return shared secret length or < 0 for error
194 * - Unwrapped CEK in EKEY. If any, wrapped CEK in "wrapped".
196 * - Caller responsibility to cleanse EKEY.
200 lws_jwe_encrypt_ecdh(struct lws_jwe *jwe, char *temp, int *temp_len,
203 uint8_t shared_secret[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES],
204 derived[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
205 int m, n, ret = -1, ot = *temp_len, ss_len = sizeof(shared_secret),
206 // kw_hlen = lws_genhash_size(jwe->jose.alg->hash_type),
207 enc_hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type),
208 ekbytes = 32; //jwe->jose.alg->keybits_fixed / 8;
209 struct lws_genec_ctx ecctx;
210 struct lws_jwk *ephem = &jwe->jose.recipient[jwe->recip].jwk_ephemeral;
212 if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_EC) {
213 lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);
218 ephem->kty = LWS_GENCRYPTO_KTY_EC;
219 ephem->private_key = 1;
221 /* Generate jose.jwk_ephemeral on the peer public key curve */
223 if (lws_genecdh_create(&ecctx, jwe->jws.context, NULL))
226 /* ephemeral context gets random key on same curve as recip pubkey */
227 if (lws_genecdh_new_keypair(&ecctx, LDHS_OURS, (const char *)
228 jwe->jws.jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
232 /* peer context gets js->jwk key */
233 if (lws_genecdh_set_key(&ecctx, jwe->jws.jwk->e, LDHS_THEIRS)) {
234 lwsl_err("%s: setting peer pubkey failed\n", __func__);
238 /* combine our ephemeral key and the peer pubkey to get the secret */
240 if (lws_genecdh_compute_shared_secret(&ecctx, shared_secret, &ss_len)) {
241 lwsl_notice("%s: lws_genecdh_compute_shared_secret failed\n",
248 * The private part of the ephemeral key is finished with...
249 * cleanse and free it. We need to keep the public part around so we
250 * can publish it with the JWE as "epk".
253 lws_explicit_bzero(ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].buf,
254 ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].len);
255 lws_free_set_NULL(ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].buf);
256 ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].len = 0;
257 ephem->private_key = 0;
260 * Derive the CEK from the shared secret... amount of bytes written to
261 * derived matches bitcount in jwe->jose.enc_alg->keybits_fixed
263 * In Direct Key Agreement mode, the output of the Concat KDF MUST be a
264 * key of the same length as that used by the "enc" algorithm.
267 if (lws_jwa_concat_kdf(jwe,
268 jwe->jose.alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE,
269 derived, shared_secret, ss_len)) {
270 lwsl_notice("%s: lws_jwa_concat_kdf failed\n", __func__);
275 /* in P-521 case, we get a 66-byte shared secret for a 64-byte key */
276 if (ss_len < enc_hlen) {
277 lwsl_err("%s: concat KDF bad derived key len %d\n", __func__,
283 * For "ECDH-ES", that was it, and we use what we just wrapped in
284 * wrapped as the CEK without publishing it.
286 * For "ECDH-ES-AES[128,192,256]KW", we generate a new, random CEK and
287 * then wrap it using the key we just wrapped, and make the wrapped
288 * version available in EKEY.
291 if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) {
292 struct lws_gencrypto_keyelem el;
293 struct lws_genaes_ctx aesctx;
295 /* generate the actual CEK in cek */
297 if (lws_get_random(jwe->jws.context, cek, enc_hlen) != enc_hlen) {
298 lwsl_err("Problem getting random\n");
302 /* wrap with the derived key */
305 el.len = enc_hlen / 2;
307 if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_KW, &el,
310 lwsl_notice("%s: lws_genaes_create\n", __func__);
314 /* wrap CEK into EKEY */
316 n = lws_genaes_crypt(&aesctx, cek, enc_hlen,
317 (void *)jwe->jws.map.buf[LJWE_EKEY],
318 NULL, NULL, NULL, 0);
319 m = lws_genaes_destroy(&aesctx, NULL, 0);
321 lwsl_err("%s: encrypt cek fail\n", __func__);
325 lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
329 jwe->jws.map.len[LJWE_EKEY] = enc_hlen + 8;
331 /* Wrapped CEK is in EKEY. Random CEK is in cek. */
333 } else /* direct derived CEK is in cek */
334 memcpy(cek, derived, enc_hlen);
336 /* rewrite the protected JOSE header to have the epk pieces */
338 jwe->jws.map.buf[LJWE_JOSE] = temp + (ot - *temp_len);
340 m = n = lws_snprintf(temp + (ot - *temp_len), *temp_len,
341 "{\"alg\":\"%s\", \"enc\":\"%s\", \"epk\":",
342 jwe->jose.alg->alg, jwe->jose.enc_alg->alg);
345 n = lws_jwk_export(ephem, 0, temp + (ot - *temp_len), temp_len);
347 lwsl_err("%s: ephemeral export failed\n", __func__);
352 n = lws_snprintf(temp + (ot - *temp_len), *temp_len, "}");
355 jwe->jws.map.len[LJWE_JOSE] = m;
357 /* create a b64 version of the JOSE header, needed later for AAD */
359 if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE,
360 temp + (ot - *temp_len), temp_len,
361 jwe->jws.map.buf[LJWE_JOSE],
362 jwe->jws.map.len[LJWE_JOSE]))
368 lws_genec_destroy(&ecctx);
370 /* cleanse the shared secret (watch out for cek at parent too) */
371 lws_explicit_bzero(shared_secret, ekbytes);
372 lws_explicit_bzero(derived, ekbytes);
378 lws_jwe_encrypt_ecdh_cbc_hs(struct lws_jwe *jwe, char *temp, int *temp_len)
380 int ss_len, // kw_hlen = lws_genhash_size(jwe->jose.alg->hash_type),
381 enc_hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type);
382 uint8_t cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
383 int ekbytes = jwe->jose.alg->keybits_fixed / 8;
384 int n, ot = *temp_len, ret = -1;
386 /* if we will produce an EKEY, make space for it */
388 if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) {
389 if (lws_jws_alloc_element(&jwe->jws.map, LJWE_EKEY,
390 temp + (ot - *temp_len), temp_len,
395 /* decrypt the CEK */
397 ss_len = lws_jwe_encrypt_ecdh(jwe, temp + (ot - *temp_len), temp_len, cek);
399 lwsl_err("%s: lws_jwe_encrypt_ecdh failed\n", __func__);
403 /* cek contains the unwrapped CEK. EKEY may contain wrapped CEK */
405 /* make space for the payload encryption pieces */
407 if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG,
408 temp + (ot - *temp_len),
409 temp_len, enc_hlen / 2, 0))
412 if (lws_jws_alloc_element(&jwe->jws.map, LJWE_IV,
413 temp + (ot - *temp_len),
414 temp_len, LWS_JWE_AES_IV_BYTES, 0))
417 /* Perform the authenticated encryption on CTXT...
418 * ...the AAD is b64u(protected JOSE header) */
420 n = lws_jwe_encrypt_cbc_hs(jwe, cek,
421 (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
422 jwe->jws.map_b64.len[LJWE_JOSE]);
424 lwsl_notice("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__);
431 /* if fail or direct CEK, cleanse and remove EKEY */
432 if (ret || jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE) {
433 if (jwe->jws.map.len[LJWE_EKEY])
434 lws_explicit_bzero((void *)jwe->jws.map.buf[LJWE_EKEY],
435 jwe->jws.map.len[LJWE_EKEY]);
436 jwe->jws.map.len[LJWE_EKEY] = 0;
439 lws_explicit_bzero(cek, ekbytes);
445 * jwe->jws.jwk is recipient private key
447 * If kw mode, then EKEY is the wrapped CEK
453 lws_jwe_auth_and_decrypt_ecdh(struct lws_jwe *jwe)
455 uint8_t shared_secret[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES],
456 derived[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
457 int ekbytes = jwe->jose.enc_alg->keybits_fixed / 8,
458 enc_hlen = lws_genhmac_size(jwe->jose.enc_alg->hmac_type);
459 struct lws_genec_ctx ecctx;
460 int n, ret = -1, ss_len = sizeof(shared_secret);
462 if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_EC) {
463 lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);
468 if (jwe->jose.recipient[jwe->recip].jwk_ephemeral.kty !=
469 LWS_GENCRYPTO_KTY_EC) {
470 lwsl_err("%s: missing epk\n", __func__);
476 * Recompute the shared secret...
478 * - direct: it's the CEK
480 * - aeskw: apply it as AES keywrap to EKEY to get the CEK
483 /* Generate jose.jwk_ephemeral on the peer public key curve */
485 if (lws_genecdh_create(&ecctx, jwe->jws.context, NULL))
488 /* Load our private key into our side of the ecdh context */
490 if (lws_genecdh_set_key(&ecctx, jwe->jws.jwk->e, LDHS_OURS)) {
491 lwsl_err("%s: setting our private key failed\n", __func__);
495 /* Import the ephemeral public key into the peer side */
496 if (lws_genecdh_set_key(&ecctx,
497 jwe->jose.recipient[jwe->recip].jwk_ephemeral.e,
499 lwsl_err("%s: setting epk pubkey failed\n", __func__);
503 /* combine their ephemeral key and our private key to get the secret */
505 if (lws_genecdh_compute_shared_secret(&ecctx, shared_secret, &ss_len)) {
506 lwsl_notice("%s: lws_genecdh_compute_shared_secret failed\n",
512 lws_genec_destroy(&ecctx);
514 if (ss_len < enc_hlen) {
515 lwsl_err("%s: ss_len %d ekbytes %d\n", __func__, ss_len, enc_hlen);
520 * Derive the CEK from the shared secret... amount of bytes written to
521 * cek[] matches bitcount in jwe->jose.enc_alg->keybits_fixed
524 if (lws_jwa_concat_kdf(jwe,
525 jwe->jose.alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE,
526 derived, shared_secret, ss_len)) {
527 lwsl_notice("%s: lws_jwa_concat_kdf failed\n", __func__);
533 * "ECDH-ES": derived is the CEK
534 * "ECDH-ES-AES[128,192,256]KW": wrapped key is in EKEY,
535 * "derived" contains KEK
538 if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) {
539 struct lws_gencrypto_keyelem el;
540 struct lws_genaes_ctx aesctx;
543 /* Confirm space for EKEY */
545 if (jwe->jws.map.len[LJWE_EKEY] < (unsigned int)enc_hlen) {
546 lwsl_err("%s: missing EKEY\n", __func__);
551 /* unwrap with the KEK we derived */
554 el.len = enc_hlen / 2;
556 if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_KW,
559 lwsl_notice("%s: lws_genaes_create\n", __func__);
563 /* decrypt the EKEY to end up with CEK in "shared_secret" */
565 n = lws_genaes_crypt(&aesctx,
566 (const uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
567 jwe->jws.map.len[LJWE_EKEY],
568 (uint8_t *)shared_secret,
569 NULL, NULL, NULL, 0);
570 m = lws_genaes_destroy(&aesctx, NULL, 0);
572 lwsl_err("%s: decrypt cek fail\n", __func__);
576 lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
580 memcpy(shared_secret, derived, enc_hlen);
582 /* either way, the recovered CEK is in shared_secret */
584 if (lws_jwe_auth_and_decrypt_cbc_hs(jwe, shared_secret,
585 (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
586 jwe->jws.map_b64.len[LJWE_JOSE]) < 0) {
587 lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs fail\n", __func__);
591 /* if all went well, then CTXT is now the plaintext */
595 /* cleanse wrapped on stack that contained the CEK / wrapped key */
596 lws_explicit_bzero(derived, ekbytes);
597 /* cleanse the shared secret */
598 lws_explicit_bzero(shared_secret, ekbytes);
604 lws_jwe_auth_and_decrypt_ecdh_cbc_hs(struct lws_jwe *jwe,
605 char *temp, int *temp_len)
607 /* create a b64 version of the JOSE header, needed later for AAD */
609 if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE,
611 jwe->jws.map.buf[LJWE_JOSE],
612 jwe->jws.map.len[LJWE_JOSE]))
615 return lws_jwe_auth_and_decrypt_ecdh(jwe);