1
0

dec.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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 <unistd.h>
  20. #include <string.h>
  21. #define SUMMARY "Decrypts a JWE using the supplied JWKs and outputs plaintext"
  22. typedef struct {
  23. jcmd_opt_io_t io;
  24. json_t *keys;
  25. bool pwd;
  26. } jcmd_opt_t;
  27. static const char *prefix =
  28. "jose jwe dec -i JWE [-I CT] -k JWK [-p] [-O PT]\n\n" SUMMARY;
  29. static const jcmd_doc_t doc_password[] = {
  30. { .doc="Prompt for a decryption password, if necessary" },
  31. {}
  32. };
  33. static const jcmd_cfg_t cfgs[] = {
  34. {
  35. .opt = { "input", required_argument, .val = 'i' },
  36. .off = offsetof(jcmd_opt_t, io),
  37. .set = jcmd_opt_io_set_input,
  38. .doc = jcmd_jwe_doc_input,
  39. },
  40. {
  41. .opt = { "detached", required_argument, .val = 'I' },
  42. .off = offsetof(jcmd_opt_t, io.detached),
  43. .set = jcmd_opt_set_ifile,
  44. .doc = jcmd_jwe_doc_detached,
  45. },
  46. {
  47. .opt = { "password", no_argument, .val = 'p' },
  48. .off = offsetof(jcmd_opt_t, pwd),
  49. .set = jcmd_opt_set_flag,
  50. .doc = doc_password,
  51. },
  52. {
  53. .opt = { "key", required_argument, .val = 'k' },
  54. .off = offsetof(jcmd_opt_t, keys),
  55. .set = jcmd_opt_set_jwks,
  56. .doc = jcmd_doc_key,
  57. },
  58. {
  59. .opt = { "detach", required_argument, .val = 'O' },
  60. .off = offsetof(jcmd_opt_t, io.detach),
  61. .set = jcmd_opt_set_ofile,
  62. .doc = jcmd_jwe_doc_input,
  63. .def = "-",
  64. },
  65. {}
  66. };
  67. static void
  68. jcmd_opt_cleanup(jcmd_opt_t *opt)
  69. {
  70. jcmd_opt_io_cleanup(&opt->io);
  71. json_decref(opt->keys);
  72. }
  73. static bool
  74. header_has_pbes2(const json_t *jwe, const json_t *rcp)
  75. {
  76. json_auto_t *hdr = NULL;
  77. const char *alg = NULL;
  78. hdr = jose_jwe_hdr(jwe, rcp);
  79. if (!hdr)
  80. return false;
  81. if (json_unpack(hdr, "{s:s}", "alg", &alg) < 0)
  82. return false;
  83. return strncmp(alg, "PBES2", strlen("PBES2")) == 0;
  84. }
  85. static bool
  86. jwe_has_pbes2(const json_t *jwe)
  87. {
  88. json_t *rcps = NULL;
  89. rcps = json_object_get(jwe, "recipients");
  90. if (!json_is_array(rcps))
  91. return header_has_pbes2(jwe, jwe);
  92. for (size_t i = 0; i < json_array_size(rcps); i++) {
  93. if (header_has_pbes2(jwe, json_array_get(rcps, i)))
  94. return true;
  95. }
  96. return false;
  97. }
  98. static json_t *
  99. unwrap(const json_t *jwe, const json_t *jwks, bool prompt)
  100. {
  101. json_auto_t *cek = NULL;
  102. cek = jose_jwe_dec_jwk(NULL, jwe, NULL, jwks);
  103. if (!cek && jwe_has_pbes2(jwe) && prompt) {
  104. const char *pwd = NULL;
  105. pwd = jwe_getpass("Please enter decryption password: ");
  106. if (pwd) {
  107. json_auto_t *jwk = json_string(pwd);
  108. cek = jose_jwe_dec_jwk(NULL, jwe, NULL, jwk);
  109. }
  110. }
  111. return json_incref(cek);
  112. }
  113. static int
  114. jcmd_jwe_dec(int argc, char *argv[])
  115. {
  116. jcmd_opt_auto_t opt = { .io.fields = jcmd_jwe_fields };
  117. jose_io_auto_t *dec = NULL;
  118. jose_io_auto_t *out = NULL;
  119. json_auto_t *cek = NULL;
  120. if (!jcmd_opt_parse(argc, argv, cfgs, &opt, prefix))
  121. return EXIT_FAILURE;
  122. if (!opt.io.obj) {
  123. fprintf(stderr, "Invalid JWE!\n");
  124. return EXIT_FAILURE;
  125. }
  126. if (json_array_size(opt.keys) == 0 && !opt.pwd) {
  127. fprintf(stderr, "MUST specify a JWK in non-interactive mode!\n\n");
  128. return EXIT_FAILURE;
  129. }
  130. cek = unwrap(opt.io.obj, opt.keys, opt.pwd);
  131. if (!cek) {
  132. fprintf(stderr, "Unwrapping failed!\n");
  133. return EXIT_FAILURE;
  134. }
  135. out = jose_io_file(NULL, opt.io.detach);
  136. if (!out)
  137. return EXIT_FAILURE;
  138. dec = jose_jwe_dec_cek_io(NULL, opt.io.obj, cek, out);
  139. if (!dec)
  140. return EXIT_FAILURE;
  141. if (!opt.io.detached) {
  142. jose_io_auto_t *b64 = NULL;
  143. b64 = jose_b64_dec_io(dec);
  144. if (!b64)
  145. return EXIT_FAILURE;
  146. jose_io_auto(&dec);
  147. dec = jose_io_incref(b64);
  148. }
  149. if (opt.io.detached || opt.io.input) {
  150. FILE *f = opt.io.detached ? opt.io.detached : opt.io.input;
  151. for (int c = fgetc(f); c != EOF; c = fgetc(f)) {
  152. uint8_t b = c;
  153. if (!opt.io.detached && b == '.')
  154. break;
  155. if (!dec->feed(dec, &b, sizeof(b)))
  156. return EXIT_FAILURE;
  157. }
  158. for (int c = 0; opt.io.detached && opt.io.input && c != EOF && c != '.'; )
  159. c = fgetc(opt.io.input);
  160. } else {
  161. const char *ct = NULL;
  162. size_t ctl = 0;
  163. if (json_unpack(opt.io.obj, "{s:s%}", "ciphertext", &ct, &ctl) < 0)
  164. return EXIT_FAILURE;
  165. if (!dec->feed(dec, ct, ctl))
  166. return EXIT_FAILURE;
  167. }
  168. if (opt.io.input) {
  169. if (json_object_set_new(opt.io.obj, "tag",
  170. jcmd_compact_field(opt.io.input)) < 0) {
  171. fprintf(stderr, "Error reading last compact field!\n");
  172. return EXIT_FAILURE;
  173. }
  174. }
  175. if (!dec->done(dec))
  176. return EXIT_FAILURE;
  177. return EXIT_SUCCESS;
  178. }
  179. JCMD_REGISTER(SUMMARY, jcmd_jwe_dec, "jwe", "dec")