enc.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
  2. /*
  3. * Copyright 2016 Red Hat, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. #include "jwe.h"
  18. #include "pwd.h"
  19. #include <string.h>
  20. #include <unistd.h>
  21. #define SUMMARY "Encrypts plaintext using one or more JWKs and outputs a JWE"
  22. typedef struct {
  23. jcmd_opt_io_t io;
  24. json_t *keys;
  25. json_t *rcps;
  26. } jcmd_opt_t;
  27. static const char *prefix =
  28. "jose jwe enc [-i JWE] -I PT [-r RCP] -k JWK [-p] [-o JWE] [-O CT] [-c]\n\n" SUMMARY;
  29. static json_t *
  30. prompt(void)
  31. {
  32. const char *c = NULL;
  33. char *p = NULL;
  34. while (!p || !c || strcmp(p, c) != 0) {
  35. free(p);
  36. p = strdup(jwe_getpass("Please enter an encryption password: "));
  37. if (!p)
  38. continue;
  39. if (strlen(p) < 8) {
  40. fprintf(stderr, "Password too short!\n");
  41. continue;
  42. }
  43. c = jwe_getpass("Please re-enter the previous password: ");
  44. }
  45. free(p);
  46. return json_string(c);
  47. }
  48. static bool
  49. opt_set_password(const jcmd_cfg_t *cfg, void *vopt, const char *arg)
  50. {
  51. json_t **keys = vopt;
  52. if (!*keys)
  53. *keys = json_array();
  54. return json_array_append_new(*keys, json_null()) == 0;
  55. }
  56. static const jcmd_doc_t doc_recipient[] = {
  57. { .arg = "FILE", .doc="Read JWE recipient template from FILE" },
  58. { .arg = "-", .doc="Read JWE recipient template from standard input" },
  59. {}
  60. };
  61. static const jcmd_doc_t doc_password[] = {
  62. { .doc="Prompt for an encryption password" },
  63. {}
  64. };
  65. static const jcmd_cfg_t cfgs[] = {
  66. {
  67. .opt = { "input", required_argument, .val = 'i' },
  68. .off = offsetof(jcmd_opt_t, io),
  69. .set = jcmd_opt_io_set_input,
  70. .doc = jcmd_jwe_doc_input,
  71. .def = "{}",
  72. },
  73. {
  74. .opt = { "detached", required_argument, .val = 'I' },
  75. .off = offsetof(jcmd_opt_t, io.detached),
  76. .set = jcmd_opt_set_ifile,
  77. .doc = jcmd_jwe_doc_detached,
  78. },
  79. {
  80. .opt = { "recipient", required_argument, .val = 'r' },
  81. .off = offsetof(jcmd_opt_t, rcps),
  82. .set = jcmd_opt_set_jsons,
  83. .doc = doc_recipient,
  84. .def = "{}",
  85. },
  86. {
  87. .opt = { "key", required_argument, .val = 'k' },
  88. .off = offsetof(jcmd_opt_t, keys),
  89. .set = jcmd_opt_set_jwks,
  90. .doc = jcmd_doc_key,
  91. },
  92. {
  93. .opt = { "password", no_argument, .val = 'p' },
  94. .off = offsetof(jcmd_opt_t, keys),
  95. .set = opt_set_password,
  96. .doc = doc_password,
  97. },
  98. {
  99. .opt = { "output", required_argument, .val = 'o' },
  100. .off = offsetof(jcmd_opt_t, io.output),
  101. .set = jcmd_opt_set_ofile,
  102. .doc = jcmd_jwe_doc_output,
  103. .def = "-",
  104. },
  105. {
  106. .opt = { "detach", required_argument, .val = 'O' },
  107. .off = offsetof(jcmd_opt_t, io.detach),
  108. .set = jcmd_opt_set_ofile,
  109. .doc = jcmd_jwe_doc_detach,
  110. },
  111. {
  112. .opt = { "compact", no_argument, .val = 'c' },
  113. .off = offsetof(jcmd_opt_t, io.compact),
  114. .set = jcmd_opt_set_flag,
  115. .doc = jcmd_jwe_doc_compact,
  116. },
  117. {}
  118. };
  119. static void
  120. jcmd_opt_cleanup(jcmd_opt_t *opt)
  121. {
  122. jcmd_opt_io_cleanup(&opt->io);
  123. json_decrefp(&opt->keys);
  124. json_decrefp(&opt->rcps);
  125. }
  126. static bool
  127. opt_validate(jcmd_opt_t *opt)
  128. {
  129. size_t nkeys = json_array_size(opt->keys);
  130. if (nkeys == 0) {
  131. fprintf(stderr, "Must specify a JWK or password!\n");
  132. return false;
  133. } else if (nkeys > 1 && opt->io.compact) {
  134. fprintf(stderr, "Requested compact format with >1 recipient!\n");
  135. return false;
  136. }
  137. if (!opt->io.detached) {
  138. fprintf(stderr, "Must specify detached input!\n");
  139. return false;
  140. }
  141. if (json_array_remove(opt->rcps, 0) < 0)
  142. return false;
  143. if (json_array_size(opt->keys) < json_array_size(opt->rcps)) {
  144. fprintf(stderr, "Specified more recipients than keys!\n");
  145. return false;
  146. }
  147. while (json_array_size(opt->rcps) < json_array_size(opt->keys)) {
  148. if (json_array_append_new(opt->rcps, json_object()) < 0)
  149. return false;
  150. }
  151. return true;
  152. }
  153. static json_t *
  154. wrap(jcmd_opt_t *opt)
  155. {
  156. json_auto_t *cek = json_object();
  157. for (size_t i = 0; i < json_array_size(opt->keys); i++) {
  158. json_auto_t *jwk = json_incref(json_array_get(opt->keys, i));
  159. json_t *rcp = json_array_get(opt->rcps, i);
  160. if (json_is_null(jwk)) {
  161. json_decref(jwk);
  162. jwk = prompt();
  163. }
  164. if (!jose_jwe_enc_jwk(NULL, opt->io.obj, rcp, jwk, cek)) {
  165. fprintf(stderr, "Wrapping failed!\n");
  166. return NULL;
  167. }
  168. }
  169. if (opt->io.compact) {
  170. json_t *jh = NULL;
  171. jh = jose_jwe_hdr(opt->io.obj, opt->io.obj);
  172. if (!jh)
  173. return NULL;
  174. if (json_object_set_new(opt->io.obj, "protected", jh) < 0)
  175. return NULL;
  176. if (json_object_get(opt->io.obj, "unprotected") &&
  177. json_object_del(opt->io.obj, "unprotected") < 0)
  178. return NULL;
  179. if (json_object_get(opt->io.obj, "header") &&
  180. json_object_del(opt->io.obj, "header") < 0)
  181. return NULL;
  182. }
  183. return json_incref(cek);
  184. }
  185. static int
  186. jcmd_jwe_enc(int argc, char *argv[])
  187. {
  188. jcmd_opt_auto_t opt = { .io.fields = jcmd_jwe_fields };
  189. jose_io_auto_t *out = NULL;
  190. jose_io_auto_t *enc = NULL;
  191. json_auto_t *cek = NULL;
  192. if (!jcmd_opt_parse(argc, argv, cfgs, &opt, prefix))
  193. return EXIT_FAILURE;
  194. if (!opt_validate(&opt))
  195. return EXIT_FAILURE;
  196. cek = wrap(&opt);
  197. if (!cek)
  198. return EXIT_FAILURE;
  199. out = jose_io_file(NULL, opt.io.detach ? opt.io.detach : opt.io.output);
  200. if (!out)
  201. return EXIT_FAILURE;
  202. if (!opt.io.detach) {
  203. jose_io_auto_t *b64 = NULL;
  204. b64 = jose_b64_enc_io(out);
  205. if (!b64)
  206. return EXIT_FAILURE;
  207. jose_io_auto(&out);
  208. out = jose_io_incref(b64);
  209. }
  210. enc = jose_jwe_enc_cek_io(NULL, opt.io.obj, cek, out);
  211. if (!enc)
  212. return EXIT_FAILURE;
  213. if (!opt.io.detached) {
  214. jose_io_auto_t *b64 = NULL;
  215. b64 = jose_b64_dec_io(enc);
  216. if (!b64)
  217. return EXIT_FAILURE;
  218. jose_io_auto(&enc);
  219. enc = jose_io_incref(b64);
  220. }
  221. if (opt.io.compact) {
  222. for (size_t i = 0; i < 3; i++) {
  223. const char *k = jcmd_jwe_fields[i].name;
  224. const char *v = NULL;
  225. if (json_unpack(opt.io.obj, "{s?s}", k, &v) < 0)
  226. return EXIT_FAILURE;
  227. fprintf(opt.io.output, "%s.", v ? v : "");
  228. }
  229. } else {
  230. fprintf(opt.io.output, "{");
  231. if (!opt.io.detach)
  232. fprintf(opt.io.output, "\"ciphertext\":\"");
  233. }
  234. if (opt.io.detached || opt.io.input) {
  235. FILE *f = opt.io.detached ? opt.io.detached : opt.io.input;
  236. for (int c = fgetc(f); c != EOF; c = fgetc(f)) {
  237. uint8_t b = c;
  238. if (!opt.io.detached && b == '.')
  239. break;
  240. if (!enc->feed(enc, &b, sizeof(b)))
  241. return EXIT_FAILURE;
  242. }
  243. for (int c = 0; opt.io.detached && opt.io.input && c != EOF && c != '.'; )
  244. c = fgetc(opt.io.input);
  245. } else {
  246. const char *ct = NULL;
  247. size_t ctl = 0;
  248. if (json_unpack(opt.io.obj, "{s:s%}", "ciphertext", &ct, &ctl) < 0)
  249. return EXIT_FAILURE;
  250. if (!enc->feed(enc, ct, ctl))
  251. return EXIT_FAILURE;
  252. }
  253. if (opt.io.input) {
  254. if (json_object_set_new(opt.io.obj, "tag",
  255. jcmd_compact_field(opt.io.input)) < 0) {
  256. fprintf(stderr, "Error reading last compact field!\n");
  257. return EXIT_FAILURE;
  258. }
  259. }
  260. if (!enc->done(enc))
  261. return EXIT_FAILURE;
  262. if (opt.io.compact) {
  263. const char *v = NULL;
  264. if (json_unpack(opt.io.obj, "{s:s}", "tag", &v) < 0) {
  265. fprintf(stderr, "Missing tag parameter!\n");
  266. return false;
  267. }
  268. fprintf(opt.io.output, ".%s", v);
  269. } else {
  270. if (!opt.io.detach)
  271. fprintf(opt.io.output, "\",");
  272. json_dumpf(opt.io.obj, opt.io.output,
  273. JSON_EMBED | JSON_COMPACT | JSON_SORT_KEYS);
  274. fprintf(opt.io.output, "}");
  275. }
  276. if (isatty(fileno(opt.io.output)))
  277. fprintf(opt.io.output, "\n");
  278. return EXIT_SUCCESS;
  279. }
  280. JCMD_REGISTER(SUMMARY, jcmd_jwe_enc, "jwe", "enc")