mirror of
https://libwebsockets.org/repo/libwebsockets
synced 2024-12-25 23:00:12 +00:00
289 lines
6.1 KiB
C
289 lines
6.1 KiB
C
/*
|
|
* lws-crypto-jwe
|
|
*
|
|
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
|
*
|
|
* 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 */
|
|
#if defined(LWS_WITH_NETWORK)
|
|
info.port = CONTEXT_PORT_NO_LISTEN;
|
|
#endif
|
|
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] = (uint32_t)lws_snprintf(
|
|
(char *)jwe.jws.map.buf[LJWS_JOSE], (unsigned int)temp_len,
|
|
"{\"alg\":\"%s\",\"enc\":\"%s\"}", p, sp + 1);
|
|
|
|
enc = 1;
|
|
}
|
|
|
|
in = lws_concat_temp(temp, temp_len);
|
|
n = (int)read(0, in, (unsigned int)temp_len);
|
|
if (n < 0) {
|
|
lwsl_err("Problem reading from stdin\n");
|
|
return 1;
|
|
}
|
|
|
|
/* account for padding as well */
|
|
|
|
temp_len -= (int)lws_gencrypto_padded_length(LWS_AES_CBC_BLOCKLEN, (unsigned int)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] = (uint32_t)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, (unsigned int)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,
|
|
#if defined(WIN32)
|
|
(unsigned int)
|
|
#endif
|
|
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;
|
|
}
|