| /* |
| * lws-crypto-jwe |
| * |
| * Written in 2010-2019 by Andy Green <[email protected]> |
| * |
| * This file is made available under the Creative Commons CC0 1.0 |
| * Universal Public Domain Dedication. |
| */ |
| |
| #include <libwebsockets.h> |
| #include <sys/types.h> |
| #include <fcntl.h> |
| |
| /* |
| * handles escapes and line wrapping suitable for use |
| * defining a C char array ( -c option ) |
| */ |
| |
| static void |
| format_c(const char *key) |
| { |
| const char *k = key; |
| int seq = 0; |
| |
| while (*k) { |
| if (*k == '{') { |
| putchar('\"'); |
| putchar('{'); |
| putchar('\"'); |
| putchar('\n'); |
| putchar('\t'); |
| putchar('\"'); |
| k++; |
| seq = 0; |
| continue; |
| } |
| if (*k == '}') { |
| putchar('\"'); |
| putchar('\n'); |
| putchar('\"'); |
| putchar('}'); |
| putchar('\"'); |
| putchar('\n'); |
| k++; |
| seq = 0; |
| continue; |
| } |
| if (*k == '\"') { |
| putchar('\\'); |
| putchar('\"'); |
| seq += 2; |
| k++; |
| continue; |
| } |
| if (*k == ',') { |
| putchar(','); |
| putchar('\"'); |
| putchar('\n'); |
| putchar('\t'); |
| putchar('\"'); |
| k++; |
| seq = 0; |
| continue; |
| } |
| putchar(*k); |
| seq++; |
| if (seq >= 60) { |
| putchar('\"'); |
| putchar('\n'); |
| putchar('\t'); |
| putchar(' '); |
| putchar('\"'); |
| seq = 1; |
| } |
| k++; |
| } |
| } |
| |
| #define MAX_SIZE (4 * 1024 * 1024) |
| char temp[MAX_SIZE], compact[MAX_SIZE]; |
| |
| int main(int argc, const char **argv) |
| { |
| int n, enc = 0, result = 0, |
| logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; |
| char *in; |
| struct lws_context_creation_info info; |
| int temp_len = sizeof(temp); |
| struct lws_context *context; |
| struct lws_jwe jwe; |
| const char *p; |
| |
| if ((p = lws_cmdline_option(argc, argv, "-d"))) |
| logs = atoi(p); |
| |
| lws_set_log_level(logs, NULL); |
| lwsl_user("LWS JWE example tool\n"); |
| |
| memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ |
| info.port = CONTEXT_PORT_NO_LISTEN; |
| info.options = 0; |
| |
| context = lws_create_context(&info); |
| if (!context) { |
| lwsl_err("lws init failed\n"); |
| return 1; |
| } |
| |
| lws_jwe_init(&jwe, context); |
| |
| /* if encrypting, set the ciphers */ |
| |
| if ((p = lws_cmdline_option(argc, argv, "-e"))) { |
| char *sp = strchr(p, ' '); |
| |
| if (!sp) { |
| lwsl_err("format: -e \"<cek cipher alg> " |
| "<payload enc alg>\", eg, " |
| "-e \"RSA1_5 A128CBC-HS256\"\n"); |
| |
| return 1; |
| } |
| *sp = '\0'; |
| if (lws_gencrypto_jwe_alg_to_definition(p, &jwe.jose.alg)) { |
| lwsl_err("Unknown cipher alg %s\n", p); |
| return 1; |
| } |
| if (lws_gencrypto_jwe_enc_to_definition(sp + 1, &jwe.jose.enc_alg)) { |
| lwsl_err("Unknown payload enc alg %s\n", sp + 1); |
| return 1; |
| } |
| |
| /* create JOSE header, also needed for output */ |
| |
| if (lws_jws_alloc_element(&jwe.jws.map, LJWS_JOSE, |
| lws_concat_temp(temp, temp_len), |
| &temp_len, strlen(p) + |
| strlen(sp + 1) + 32, 0)) { |
| lwsl_err("%s: temp space too small\n", __func__); |
| return 1; |
| } |
| |
| jwe.jws.map.len[LJWS_JOSE] = lws_snprintf( |
| (char *)jwe.jws.map.buf[LJWS_JOSE], temp_len, |
| "{\"alg\":\"%s\",\"enc\":\"%s\"}", p, sp + 1); |
| |
| enc = 1; |
| } |
| |
| in = lws_concat_temp(temp, temp_len); |
| n = read(0, in, temp_len); |
| if (n < 0) { |
| lwsl_err("Problem reading from stdin\n"); |
| return 1; |
| } |
| |
| /* account for padding as well */ |
| |
| temp_len -= lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, n); |
| |
| /* grab the key */ |
| |
| if ((p = lws_cmdline_option(argc, argv, "-k"))) { |
| if (lws_jwk_load(&jwe.jwk, p, NULL, NULL)) { |
| lwsl_err("%s: problem loading JWK %s\n", __func__, p); |
| |
| return 1; |
| } |
| } else { |
| lwsl_err("-k <jwk file> is required\n"); |
| |
| return 1; |
| } |
| |
| if (enc) { |
| |
| /* point CTXT to the plaintext we read from stdin */ |
| |
| jwe.jws.map.buf[LJWE_CTXT] = in; |
| jwe.jws.map.len[LJWE_CTXT] = n; |
| |
| /* |
| * Create a random CEK and set EKEY to it |
| * CEK size is determined by hash / hmac size |
| */ |
| |
| n = lws_gencrypto_bits_to_bytes(jwe.jose.enc_alg->keybits_fixed); |
| if (lws_jws_randomize_element(context, &jwe.jws.map, LJWE_EKEY, |
| lws_concat_temp(temp, temp_len), |
| &temp_len, n, |
| LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) { |
| lwsl_err("Problem getting random\n"); |
| goto bail1; |
| } |
| |
| /* perform the encryption of the CEK and the plaintext */ |
| |
| n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len), |
| &temp_len); |
| if (n < 0) { |
| lwsl_err("%s: lws_jwe_encrypt failed\n", __func__); |
| goto bail1; |
| } |
| if (lws_cmdline_option(argc, argv, "-f")) |
| /* output the JWE in flattened form */ |
| n = lws_jwe_render_flattened(&jwe, compact, |
| sizeof(compact)); |
| else |
| /* output the JWE in compact form */ |
| n = lws_jwe_render_compact(&jwe, compact, |
| sizeof(compact)); |
| |
| if (n < 0) { |
| lwsl_err("%s: lws_jwe_render failed: %d\n", |
| __func__, n); |
| goto bail1; |
| } |
| |
| if (lws_cmdline_option(argc, argv, "-c")) |
| format_c(compact); |
| else |
| if (write(1, compact, strlen(compact)) < 0) { |
| lwsl_err("Write stdout failed\n"); |
| goto bail1; |
| } |
| } else { |
| if (lws_cmdline_option(argc, argv, "-f")) { |
| if (lws_jwe_json_parse(&jwe, (uint8_t *)in, n, |
| lws_concat_temp(temp, temp_len), |
| &temp_len)) { |
| lwsl_err("%s: lws_jwe_json_parse failed\n", |
| __func__); |
| goto bail1; |
| } |
| } else |
| /* |
| * converts a compact serialization to b64 + decoded maps |
| * held in jws |
| */ |
| if (lws_jws_compact_decode(in, n, &jwe.jws.map, |
| &jwe.jws.map_b64, |
| lws_concat_temp(temp, temp_len), |
| &temp_len) != 5) { |
| lwsl_err("%s: lws_jws_compact_decode failed\n", |
| __func__); |
| goto bail1; |
| } |
| |
| /* |
| * Do the crypto according to what we parsed into the jose |
| * (information on the ciphers) and the jws (plaintext and |
| * signature info) |
| */ |
| |
| n = lws_jwe_auth_and_decrypt(&jwe, |
| lws_concat_temp(temp, temp_len), |
| &temp_len); |
| if (n < 0) { |
| lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n", |
| __func__); |
| goto bail1; |
| } |
| |
| /* if it's valid, dump the plaintext and return 0 */ |
| |
| if (write(1, jwe.jws.map.buf[LJWE_CTXT], |
| jwe.jws.map.len[LJWE_CTXT]) < 0) { |
| lwsl_err("Write stdout failed\n"); |
| goto bail1; |
| } |
| } |
| |
| result = 0; |
| |
| bail1: |
| |
| lws_jwe_destroy(&jwe); |
| |
| lws_context_destroy(context); |
| |
| return result; |
| } |