sig.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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 "jws.h"
  18. #include <string.h>
  19. #include <unistd.h>
  20. #define SUMMARY "Signs a payload using one or more JWKs and outputs a JWS"
  21. typedef struct {
  22. jcmd_opt_io_t io;
  23. json_t *keys;
  24. json_t *sigs;
  25. } jcmd_opt_t;
  26. static const char *prefix =
  27. "jose jws sig [-i JWS] [-I PAY] [-s SIG] -k JWK [-o JWS] [-O PAY] [-c]"
  28. "\n\n" SUMMARY;
  29. static const jcmd_doc_t doc_input[] = {
  30. { .arg = "JSON", .doc="Parse JWS template from JSON" },
  31. { .arg = "FILE", .doc="Read JWS template from FILE" },
  32. { .arg = "-", .doc="Read JWS template from standard input" },
  33. {}
  34. };
  35. static const jcmd_doc_t doc_signature[] = {
  36. { .arg = "JSON", .doc="Parse JWS signature template from JSON" },
  37. { .arg = "FILE", .doc="Read JWS signature template from FILE" },
  38. { .arg = "-", .doc="Read JWS signature template standard input" },
  39. {}
  40. };
  41. static const jcmd_cfg_t cfgs[] = {
  42. {
  43. .opt = { "input", required_argument, .val = 'i' },
  44. .off = offsetof(jcmd_opt_t, io),
  45. .set = jcmd_opt_io_set_input,
  46. .doc = doc_input,
  47. .def = "{}",
  48. },
  49. {
  50. .opt = { "detached", required_argument, .val = 'I' },
  51. .off = offsetof(jcmd_opt_t, io.detached),
  52. .set = jcmd_opt_set_ifile,
  53. .doc = jcmd_jws_doc_detached,
  54. },
  55. {
  56. .opt = { "signature", required_argument, .val = 's' },
  57. .off = offsetof(jcmd_opt_t, sigs),
  58. .set = jcmd_opt_set_jsons,
  59. .doc = doc_signature,
  60. .def = "{}",
  61. },
  62. {
  63. .opt = { "key", required_argument, .val = 'k' },
  64. .off = offsetof(jcmd_opt_t, keys),
  65. .set = jcmd_opt_set_jwks,
  66. .doc = jcmd_doc_key,
  67. },
  68. {
  69. .opt = { "output", required_argument, .val = 'o' },
  70. .off = offsetof(jcmd_opt_t, io.output),
  71. .set = jcmd_opt_set_ofile,
  72. .doc = jcmd_jws_doc_output,
  73. .def = "-",
  74. },
  75. {
  76. .opt = { "detach", required_argument, .val = 'O' },
  77. .off = offsetof(jcmd_opt_t, io.detach),
  78. .set = jcmd_opt_set_ofile,
  79. .doc = jcmd_jws_doc_detach,
  80. },
  81. {
  82. .opt = { "compact", no_argument, .val = 'c' },
  83. .off = offsetof(jcmd_opt_t, io.compact),
  84. .set = jcmd_opt_set_flag,
  85. .doc = jcmd_jws_doc_compact,
  86. },
  87. {}
  88. };
  89. static void
  90. jcmd_opt_cleanup(jcmd_opt_t *opt)
  91. {
  92. jcmd_opt_io_cleanup(&opt->io);
  93. json_decref(opt->keys);
  94. json_decref(opt->sigs);
  95. }
  96. static bool
  97. validate_input(jcmd_opt_t *opt)
  98. {
  99. size_t nsigs = 0;
  100. if (json_array_remove(opt->sigs, 0) < 0)
  101. return false;
  102. if (json_array_size(opt->keys) == 0) {
  103. fprintf(stderr, "At least one JWK is required to sign!\n");
  104. return false;
  105. }
  106. if (json_array_size(opt->keys) < json_array_size(opt->sigs)) {
  107. fprintf(stderr, "Specified more signature templates than JWKs!\n");
  108. return false;
  109. }
  110. nsigs += json_array_size(opt->keys);
  111. if (json_is_array(json_object_get(opt->io.obj, "signatures")))
  112. nsigs += json_array_size(json_object_get(opt->io.obj, "signatures"));
  113. if (json_object_get(opt->io.obj, "protected") ||
  114. json_object_get(opt->io.obj, "signature"))
  115. nsigs += 1;
  116. if (opt->io.compact && nsigs > 1) {
  117. fprintf(stderr, "Too many signatures for compact serialization!\n");
  118. return false;
  119. }
  120. if (json_array_size(opt->keys) < json_array_size(opt->sigs)) {
  121. fprintf(stderr, "Specified more signatures than keys!\n");
  122. return false;
  123. }
  124. while (json_array_size(opt->sigs) < json_array_size(opt->keys)) {
  125. if (json_array_append_new(opt->sigs, json_object()) < 0)
  126. return false;
  127. }
  128. return true;
  129. }
  130. static int
  131. jcmd_jws_sig(int argc, char *argv[])
  132. {
  133. jcmd_opt_auto_t opt = { .io.fields = jcmd_jws_fields };
  134. jose_io_auto_t *io = NULL;
  135. if (!jcmd_opt_parse(argc, argv, cfgs, &opt, prefix))
  136. return EXIT_FAILURE;
  137. if (!validate_input(&opt))
  138. return EXIT_FAILURE;
  139. io = jose_jws_sig_io(NULL, opt.io.obj, opt.sigs, opt.keys);
  140. if (!io)
  141. return EXIT_FAILURE;
  142. io = jcmd_jws_prep_io(&opt.io, io);
  143. if (!io)
  144. return EXIT_FAILURE;
  145. if (opt.io.compact) {
  146. const char *v = NULL;
  147. json_t *o = json_array_get(opt.sigs, 0);
  148. if (json_unpack(o, "{s?s}", "protected", &v) < 0)
  149. return EXIT_FAILURE;
  150. fprintf(opt.io.output, "%s.", v ? v : "");
  151. } else {
  152. fprintf(opt.io.output, "{");
  153. if (!opt.io.detach)
  154. fprintf(opt.io.output, "\"payload\":\"");
  155. }
  156. if (opt.io.detached || opt.io.input) {
  157. FILE *f = opt.io.detached ? opt.io.detached : opt.io.input;
  158. for (int c = fgetc(f); c != EOF; c = fgetc(f)) {
  159. uint8_t b = c;
  160. if (!opt.io.detached && b == '.')
  161. break;
  162. if (!io->feed(io, &b, sizeof(b)))
  163. return EXIT_FAILURE;
  164. }
  165. for (int c = 0; opt.io.detached && opt.io.input && c != EOF && c != '.'; )
  166. c = fgetc(opt.io.input);
  167. } else {
  168. const char *pay = NULL;
  169. size_t payl = 0;
  170. if (json_unpack(opt.io.obj, "{s?s%}", "payload", &pay, &payl) < 0)
  171. return EXIT_FAILURE;
  172. if (!io->feed(io, pay ? pay : "", payl))
  173. return EXIT_FAILURE;
  174. }
  175. if (opt.io.input) {
  176. if (json_object_set_new(opt.io.obj, "signature",
  177. jcmd_compact_field(opt.io.input)) < 0) {
  178. fprintf(stderr, "Error reading last compact field!\n");
  179. return EXIT_FAILURE;
  180. }
  181. }
  182. if (!io->done(io))
  183. return EXIT_FAILURE;
  184. if (opt.io.compact) {
  185. const char *v = NULL;
  186. if (json_unpack(opt.io.obj, "{s:s}", "signature", &v) < 0) {
  187. fprintf(stderr, "Missing signature parameter!\n");
  188. return EXIT_FAILURE;
  189. }
  190. fprintf(opt.io.output, ".%s", v);
  191. } else {
  192. if (!opt.io.detach)
  193. fprintf(opt.io.output, "\",");
  194. json_dumpf(opt.io.obj, opt.io.output,
  195. JSON_EMBED | JSON_COMPACT | JSON_SORT_KEYS);
  196. fprintf(opt.io.output, "}");
  197. }
  198. if (isatty(fileno(opt.io.output)))
  199. fprintf(opt.io.output, "\n");
  200. return EXIT_SUCCESS;
  201. }
  202. JCMD_REGISTER(SUMMARY, jcmd_jws_sig, "jws", "sig")