1
0

jws.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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. #define _GNU_SOURCE
  18. #include "misc.h"
  19. #include <jose/b64.h>
  20. #include <jose/jwk.h>
  21. #include <jose/jws.h>
  22. #include "hooks.h"
  23. #include <errno.h>
  24. #include <string.h>
  25. static const jose_hook_alg_t *
  26. find_alg(jose_cfg_t *cfg, json_t *jws, json_t *sig, const json_t *jwk)
  27. {
  28. const jose_hook_alg_t *alg = NULL;
  29. const char *halg = NULL;
  30. const char *kalg = NULL;
  31. json_auto_t *hdr = NULL;
  32. hdr = jose_jws_hdr(sig);
  33. if (!hdr)
  34. return NULL;
  35. if (json_unpack(hdr, "{s:s}", "alg", &halg) < 0) {
  36. for (alg = jose_hook_alg_list(); alg && !halg; alg = alg->next) {
  37. if (alg->kind != JOSE_HOOK_ALG_KIND_SIGN)
  38. continue;
  39. halg = alg->sign.sug(alg, cfg, jwk);
  40. }
  41. if (!halg) {
  42. jose_cfg_err(cfg, JOSE_CFG_ERR_ALG_NOINFER,
  43. "Unable to infer signing algorithm");
  44. return NULL;
  45. }
  46. alg = jose_hook_alg_find(JOSE_HOOK_ALG_KIND_SIGN, halg);
  47. if (alg) {
  48. json_t *h = NULL;
  49. h = json_object_get(sig, "protected");
  50. if (!h && json_object_set_new(sig, "protected", h = json_object()) < 0)
  51. return NULL;
  52. if (json_object_set_new(h, "alg", json_string(alg->name)) < 0)
  53. return NULL;
  54. }
  55. } else {
  56. alg = jose_hook_alg_find(JOSE_HOOK_ALG_KIND_SIGN, halg);
  57. }
  58. if (!alg) {
  59. jose_cfg_err(cfg, JOSE_CFG_ERR_ALG_NOTSUP,
  60. "Signing algorithm (%s) is not supported", halg);
  61. return NULL;
  62. }
  63. if (json_unpack((json_t *) jwk, "{s?s}", "alg", &kalg) < 0)
  64. return NULL;
  65. if (halg && kalg && strcmp(halg, kalg) != 0) {
  66. jose_cfg_err(cfg, JOSE_CFG_ERR_JWK_MISMATCH,
  67. "Algorithm mismatch (%s != %s)", halg, kalg);
  68. return NULL;
  69. }
  70. if (!jose_jwk_prm(cfg, jwk, false, alg->sign.sprm)) {
  71. jose_cfg_err(cfg, JOSE_CFG_ERR_JWK_DENIED,
  72. "JWK cannot be used to sign");
  73. return NULL;
  74. }
  75. return alg;
  76. }
  77. static void
  78. ios_auto(jose_io_t ***iosp)
  79. {
  80. jose_io_t **ios = *iosp;
  81. for (size_t i = 0; ios && ios[i]; i++)
  82. jose_io_auto(&ios[i]);
  83. free(ios);
  84. }
  85. static jose_io_t *
  86. prefix(jose_io_t *i, const json_t *sig)
  87. {
  88. jose_io_auto_t *io = i;
  89. const char *prot = NULL;
  90. size_t plen = 0;
  91. if (!io)
  92. return NULL;
  93. if (json_unpack((json_t *) sig, "{s?s%}", "protected", &prot, &plen) < 0)
  94. return NULL;
  95. if (prot && !io->feed(io, prot, plen))
  96. return NULL;
  97. if (!io->feed(io, ".", 1))
  98. return NULL;
  99. return jose_io_incref(io);
  100. }
  101. json_t *
  102. jose_jws_hdr(const json_t *sig)
  103. {
  104. json_auto_t *p = NULL;
  105. json_t *h = NULL;
  106. p = json_object_get(sig, "protected");
  107. if (!p)
  108. p = json_object();
  109. else if (json_is_object(p))
  110. p = json_deep_copy(p);
  111. else if (json_is_string(p))
  112. p = jose_b64_dec_load(p);
  113. if (!json_is_object(p))
  114. return NULL;
  115. h = json_object_get(sig, "header");
  116. if (h) {
  117. if (json_object_update_missing(p, h) == -1)
  118. return NULL;
  119. }
  120. return json_incref(p);
  121. }
  122. bool
  123. jose_jws_sig(jose_cfg_t *cfg, json_t *jws, json_t *sig, const json_t *jwk)
  124. {
  125. jose_io_auto_t *io = NULL;
  126. const char *pay = NULL;
  127. size_t payl = 0;
  128. if (json_unpack(jws, "{s:s%}", "payload", &pay, &payl) < 0) {
  129. jose_cfg_err(cfg, JOSE_CFG_ERR_JWS_INVALID,
  130. "JWS missing payload attribute");
  131. return false;
  132. }
  133. io = jose_jws_sig_io(cfg, jws, sig, jwk);
  134. return io && io->feed(io, pay, payl) && io->done(io);
  135. }
  136. jose_io_t *
  137. jose_jws_sig_io(jose_cfg_t *cfg, json_t *jws, json_t *sig, const json_t *jwk)
  138. {
  139. const jose_hook_alg_t *alg = NULL;
  140. json_auto_t *s = NULL;
  141. if (json_is_array(jwk) || json_is_array(json_object_get(jwk, "keys"))) {
  142. jose_io_t __attribute__((cleanup(ios_auto))) **ios = NULL;
  143. const json_t *key = NULL;
  144. size_t i = 0;
  145. if (!json_is_array(jwk))
  146. jwk = json_object_get(jwk, "keys");
  147. if (json_is_array(sig) && json_array_size(sig) != json_array_size(jwk))
  148. return NULL;
  149. ios = calloc(json_array_size(jwk) + 1, sizeof(*ios));
  150. if (!ios)
  151. return NULL;
  152. json_array_foreach(jwk, i, key) {
  153. json_auto_t *tmp = NULL;
  154. if (json_is_array(sig))
  155. tmp = json_incref(json_array_get(sig, i));
  156. else
  157. tmp = json_deep_copy(sig);
  158. ios[i] = jose_jws_sig_io(cfg, jws, tmp, key);
  159. if (!ios[i])
  160. return NULL;
  161. }
  162. return jose_io_multiplex(cfg, ios, true);
  163. }
  164. s = sig ? json_incref(sig) : json_object();
  165. if (!json_is_object(s)) {
  166. jose_cfg_err(cfg, EINVAL, "Parameter sig MUST be an object or NULL");
  167. return NULL;
  168. }
  169. alg = find_alg(cfg, jws, s, jwk);
  170. if (!alg)
  171. return NULL;
  172. if (!encode_protected(s))
  173. return NULL;
  174. return prefix(alg->sign.sig(alg, cfg, jws, s, jwk), s);
  175. }
  176. bool
  177. jose_jws_ver(jose_cfg_t *cfg, const json_t *jws, const json_t *sig,
  178. const json_t *jwk, bool all)
  179. {
  180. jose_io_auto_t *io = NULL;
  181. const char *pay = NULL;
  182. size_t payl = 0;
  183. if (json_unpack((json_t *) jws, "{s:s%}", "payload", &pay, &payl) < 0) {
  184. jose_cfg_err(cfg, JOSE_CFG_ERR_JWS_INVALID,
  185. "JWS missing payload attribute");
  186. return false;
  187. }
  188. io = jose_jws_ver_io(cfg, jws, sig, jwk, all);
  189. return io && io->feed(io, pay, payl) && io->done(io);
  190. }
  191. jose_io_t *
  192. jose_jws_ver_io(jose_cfg_t *cfg, const json_t *jws, const json_t *sig,
  193. const json_t *jwk, bool all)
  194. {
  195. const jose_hook_alg_t *alg = NULL;
  196. const char *kalg = NULL;
  197. const char *halg = NULL;
  198. json_auto_t *hdr = NULL;
  199. if (json_is_array(jwk) || json_is_array(json_object_get(jwk, "keys"))) {
  200. jose_io_t __attribute__((cleanup(ios_auto))) **ios = NULL;
  201. size_t j = 0;
  202. if (!json_is_array(jwk))
  203. jwk = json_object_get(jwk, "keys");
  204. if (json_is_array(sig) && json_array_size(sig) != json_array_size(jwk))
  205. return NULL;
  206. ios = calloc(json_array_size(jwk) + 1, sizeof(*ios));
  207. if (!ios)
  208. return NULL;
  209. for (size_t i = 0; i < json_array_size(jwk); i++) {
  210. const json_t *s = json_is_object(sig) ? sig : json_array_get(sig, i);
  211. const json_t *k = json_array_get(jwk, i);
  212. ios[j] = jose_jws_ver_io(cfg, jws, s, k, false);
  213. if (ios[j])
  214. j++;
  215. else if (all)
  216. return NULL;
  217. }
  218. return jose_io_multiplex(cfg, ios, all);
  219. }
  220. if (!sig) {
  221. jose_io_t __attribute__((cleanup(ios_auto))) **ios = NULL;
  222. const json_t *array = NULL;
  223. const json_t *s = NULL;
  224. size_t i = 0;
  225. size_t j = 0;
  226. array = json_object_get(jws, "signatures");
  227. if (!json_is_array(array))
  228. return jose_jws_ver_io(cfg, jws, jws, jwk, true);
  229. ios = calloc(json_array_size(array) + 1, sizeof(*ios));
  230. if (!ios)
  231. return NULL;
  232. json_array_foreach(array, i, s) {
  233. ios[j] = jose_jws_ver_io(cfg, jws, s, jwk, true);
  234. if (ios[j])
  235. j++;
  236. }
  237. return jose_io_multiplex(cfg, ios, false);
  238. } else if (!json_is_object(sig))
  239. return NULL;
  240. if (json_unpack((json_t *) jwk, "{s?s}", "alg", &kalg) < 0)
  241. return NULL;
  242. hdr = jose_jws_hdr(sig);
  243. if (!hdr)
  244. return NULL;
  245. if (json_unpack(hdr, "{s?s}", "alg", &halg) < 0)
  246. return NULL;
  247. if (!halg) {
  248. if (!kalg) {
  249. jose_cfg_err(cfg, JOSE_CFG_ERR_ALG_NOINFER,
  250. "Signature algorithm cannot be inferred");
  251. return NULL;
  252. }
  253. halg = kalg;
  254. } else if (kalg && strcmp(halg, kalg) < 0) {
  255. jose_cfg_err(cfg, JOSE_CFG_ERR_JWK_MISMATCH,
  256. "Signing algorithm mismatch (%s != %s)", halg, kalg);
  257. return NULL;
  258. }
  259. alg = jose_hook_alg_find(JOSE_HOOK_ALG_KIND_SIGN, halg);
  260. if (!alg) {
  261. jose_cfg_err(cfg, JOSE_CFG_ERR_ALG_NOTSUP,
  262. "Signing algorithm (%s) is not supported", halg);
  263. return NULL;
  264. }
  265. if (!jose_jwk_prm(cfg, jwk, false, alg->sign.vprm)) {
  266. jose_cfg_err(cfg, JOSE_CFG_ERR_JWK_DENIED,
  267. "JWK cannot be used to verify");
  268. return false;
  269. }
  270. return prefix(alg->sign.ver(alg, cfg, jws, sig, jwk), sig);
  271. }