thp.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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 "jwk.h"
  18. #include "../../lib/hooks.h"
  19. #include <string.h>
  20. #include <unistd.h>
  21. #define SUMMARY "Calculates the JWK thumbprint"
  22. typedef struct {
  23. const char *hash;
  24. const char *find;
  25. json_t *keys;
  26. FILE *output;
  27. } jcmd_opt_t;
  28. static const char *prefix =
  29. "jose jwk thp -i JWK [-a ALG] [-o THP]\n\n" SUMMARY;
  30. static bool
  31. opt_set_thp(const jcmd_cfg_t *cfg, void *vopt, const char *arg)
  32. {
  33. const char **find = vopt;
  34. *find = arg;
  35. return true;
  36. }
  37. static bool
  38. opt_set_hash(const jcmd_cfg_t *cfg, void *vopt, const char *arg)
  39. {
  40. const char **hash = vopt;
  41. if (strcmp(arg, "?") == 0) {
  42. for (const jose_hook_alg_t *a = jose_hook_alg_list(); a; a = a->next) {
  43. if (a->kind == JOSE_HOOK_ALG_KIND_HASH)
  44. fprintf(stdout, "%s\n", a->name);
  45. }
  46. exit(EXIT_SUCCESS);
  47. }
  48. if (!jose_hook_alg_find(JOSE_HOOK_ALG_KIND_HASH, arg))
  49. return false;
  50. *hash = arg;
  51. return true;
  52. }
  53. static const jcmd_doc_t doc_hash[] = {
  54. { .arg = "ALG", .doc = "Use the specified hash algorithm (case sensitive)" },
  55. { .arg = "?", .doc = "List available hash algorithms" },
  56. {}
  57. };
  58. static const jcmd_doc_t doc_output[] = {
  59. { .arg = "FILE", .doc="Write thumbprint(s) to FILE" },
  60. { .arg = "-", .doc="Write thumbprint(s) to standard input" },
  61. {}
  62. };
  63. static const jcmd_doc_t doc_find[] = {
  64. { .arg = "THP", .doc = "Search input keys for JWK with the given thumbprint" },
  65. {}
  66. };
  67. static const jcmd_cfg_t cfgs[] = {
  68. {
  69. .opt = { "input", required_argument, .val = 'i' },
  70. .off = offsetof(jcmd_opt_t, keys),
  71. .set = jcmd_opt_set_jwks,
  72. .doc = jcmd_jwk_doc_input,
  73. },
  74. {
  75. .opt = { "algorithm", required_argument, .val = 'a' },
  76. .off = offsetof(jcmd_opt_t, hash),
  77. .set = opt_set_hash,
  78. .doc = doc_hash,
  79. .def = "S256",
  80. },
  81. {
  82. .opt = { "output", required_argument, .val = 'o' },
  83. .off = offsetof(jcmd_opt_t, output),
  84. .doc = doc_output,
  85. .set = jcmd_opt_set_ofile,
  86. .def = "-",
  87. },
  88. {
  89. .opt = { "find", required_argument, .val = 'f' },
  90. .off = offsetof(jcmd_opt_t, find),
  91. .doc = doc_find,
  92. .set = opt_set_thp
  93. },
  94. {}
  95. };
  96. static void
  97. jcmd_opt_cleanup(jcmd_opt_t *opt)
  98. {
  99. jcmd_file_cleanup(&opt->output);
  100. json_decrefp(&opt->keys);
  101. }
  102. static int
  103. jcmd_jwk_thp(int argc, char *argv[])
  104. {
  105. jcmd_opt_auto_t opt = {};
  106. size_t elen = 0;
  107. size_t dlen = 0;
  108. if (!jcmd_opt_parse(argc, argv, cfgs, &opt, prefix))
  109. return EXIT_FAILURE;
  110. if (json_array_size(opt.keys) == 0) {
  111. fprintf(stderr, "Must specify JWK(Set)!\n");
  112. return EXIT_FAILURE;
  113. }
  114. dlen = jose_jwk_thp_buf(NULL, NULL, opt.hash, NULL, 0);
  115. if (dlen == SIZE_MAX) {
  116. fprintf(stderr, "Error determining hash size!\n");
  117. return EXIT_FAILURE;
  118. }
  119. elen = jose_b64_enc_buf(NULL, dlen, NULL, 0);
  120. if (elen == SIZE_MAX)
  121. return EXIT_FAILURE;
  122. for (size_t i = 0; i < json_array_size(opt.keys); i++) {
  123. const json_t *jwk = json_array_get(opt.keys, i);
  124. for (const jose_hook_alg_t *a = jose_hook_alg_list(); a; a = a->next) {
  125. uint8_t dec[dlen];
  126. char enc[elen];
  127. if (a->kind != JOSE_HOOK_ALG_KIND_HASH)
  128. continue;
  129. if (!opt.find && strcmp(opt.hash, a->name) != 0)
  130. continue;
  131. if (!jose_jwk_thp_buf(NULL, jwk, opt.hash, dec, sizeof(dec))) {
  132. fprintf(stderr, "Error making thumbprint!\n");
  133. return EXIT_FAILURE;
  134. }
  135. if (jose_b64_enc_buf(dec, dlen, enc, sizeof(enc)) != elen)
  136. return EXIT_FAILURE;
  137. if (!opt.find) {
  138. if (fwrite(enc, 1, elen, opt.output) != elen)
  139. return EXIT_FAILURE;
  140. if (json_array_size(opt.keys) > 1 ||
  141. isatty(fileno(opt.output))) {
  142. if (fprintf(opt.output, "\n") != 1)
  143. return EXIT_FAILURE;
  144. }
  145. } else if (strlen(opt.find) == elen &&
  146. strncmp(opt.find, enc, elen) == 0) {
  147. static const int flags = JSON_COMPACT | JSON_SORT_KEYS;
  148. if (json_dumpf(jwk, opt.output, flags) < 0)
  149. return EXIT_FAILURE;
  150. return EXIT_SUCCESS;
  151. }
  152. }
  153. }
  154. return opt.find ? EXIT_FAILURE : EXIT_SUCCESS;
  155. }
  156. JCMD_REGISTER(SUMMARY, jcmd_jwk_thp, "jwk", "thp")