clevis-encrypt-sss.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
  2. /*
  3. * Copyright (c) 2015 Red Hat, Inc.
  4. * Author: Nathaniel McCallum <npmccallum@redhat.com>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. * Additional permission under GPLv3 section 7:
  20. *
  21. * In the following paragraph, "GPL" means the GNU General Public
  22. * License, version 3 or any later version, and "Non-GPL Code" means
  23. * code that is governed neither by the GPL nor a license
  24. * compatible with the GPL.
  25. *
  26. * You may link the code of this Program with Non-GPL Code and convey
  27. * linked combinations including the two, provided that such Non-GPL
  28. * Code only links to the code of this Program through those well
  29. * defined interfaces identified in the file named EXCEPTION found in
  30. * the source code files (the "Approved Interfaces"). The files of
  31. * Non-GPL Code may instantiate templates or use macros or inline
  32. * functions from the Approved Interfaces without causing the resulting
  33. * work to be covered by the GPL. Only the copyright holders of this
  34. * Program may make changes or additions to the list of Approved
  35. * Interfaces.
  36. */
  37. #define _GNU_SOURCE
  38. #include "sss.h"
  39. #include <openssl/crypto.h>
  40. #include <jose/b64.h>
  41. #include <jose/jwe.h>
  42. #include <sys/epoll.h>
  43. #include <sys/types.h>
  44. #include <sys/wait.h>
  45. #include <libgen.h>
  46. #include <errno.h>
  47. #include <fcntl.h>
  48. #include <limits.h>
  49. #include <unistd.h>
  50. #include <signal.h>
  51. #include <stdbool.h>
  52. #include <stdint.h>
  53. #include <string.h>
  54. #define str_auto_t char __attribute__((cleanup(str_auto)))
  55. static void
  56. str_auto(char **str)
  57. {
  58. if (!str || !*str)
  59. return;
  60. OPENSSL_cleanse(*str, strlen(*str));
  61. free(*str);
  62. }
  63. static json_int_t
  64. npins(json_t *pins)
  65. {
  66. const char *key = NULL;
  67. json_t *val = NULL;
  68. json_int_t n = 0;
  69. json_object_foreach(pins, key, val) {
  70. if (json_is_object(val))
  71. n++;
  72. else if (json_is_array(val))
  73. n += json_array_size(val);
  74. }
  75. return n;
  76. }
  77. static json_t *
  78. encrypt_frag(json_t *sss, const char *pin, const json_t *cfg)
  79. {
  80. char *args[] = { "clevis", "encrypt", (char *) pin, NULL, NULL };
  81. json_auto_t *jwe = json_string("");
  82. str_auto_t *str = NULL;
  83. uint8_t *pnt = NULL;
  84. FILE *pipe = NULL;
  85. size_t pntl = 0;
  86. pid_t pid = 0;
  87. str = args[3] = json_dumps(cfg, JSON_SORT_KEYS | JSON_COMPACT);
  88. if (!str)
  89. return NULL;
  90. pnt = sss_point(sss, &pntl);
  91. if (!pnt)
  92. return NULL;
  93. pipe = call(args, pnt, pntl, &pid);
  94. OPENSSL_cleanse(pnt, pntl);
  95. free(pnt);
  96. if (!pipe)
  97. return NULL;
  98. while (!feof(pipe)) {
  99. char buf[1024] = {};
  100. json_t *tmp = NULL;
  101. size_t rd = 0;
  102. rd = fread(buf, 1, sizeof(buf), pipe);
  103. if (ferror(pipe)) {
  104. fclose(pipe);
  105. return NULL;
  106. }
  107. tmp = json_pack("s+%", json_string_value(jwe), buf, rd);
  108. if (!tmp) {
  109. fclose(pipe);
  110. return NULL;
  111. }
  112. json_decref(jwe);
  113. jwe = tmp;
  114. }
  115. fclose(pipe);
  116. waitpid(pid, NULL, 0);
  117. return json_incref(jwe);
  118. }
  119. static json_t *
  120. encrypt_frags(json_int_t t, json_t *pins)
  121. {
  122. const char *pname = NULL;
  123. json_auto_t *sss = NULL;
  124. json_t *pcfgs = NULL;
  125. json_t *parr = NULL;
  126. /* Generate the SSS polynomial. */
  127. sss = sss_generate(32, t);
  128. if (!sss) {
  129. fprintf(stderr, "Error generating SSS!\n");
  130. return NULL;
  131. }
  132. if (json_object_set_new(sss, "jwe", parr = json_array()) < 0)
  133. return NULL;
  134. /* Encrypt each key share with a child pin. */
  135. json_object_foreach(pins, pname, pcfgs) {
  136. json_t *pcfg = NULL;
  137. size_t i = 0;
  138. if (json_is_object(pcfgs))
  139. pcfgs = json_pack("[O]", pcfgs);
  140. else if (json_is_array(pcfgs))
  141. pcfgs = json_incref(pcfgs);
  142. else
  143. return NULL;
  144. if (json_object_set_new(pins, pname, pcfgs) < 0)
  145. return NULL;
  146. json_array_foreach(pcfgs, i, pcfg) {
  147. json_auto_t *jwe = NULL;
  148. jwe = encrypt_frag(sss, pname, pcfg);
  149. if (!jwe)
  150. return NULL;
  151. if (json_array_append(parr, jwe) < 0)
  152. return NULL;
  153. }
  154. }
  155. return json_incref(sss);
  156. }
  157. int
  158. main(int argc, char *argv[])
  159. {
  160. const char *SUMMARY = "Encrypts using a Shamir's Secret Sharing policy";
  161. jose_io_auto_t *out = NULL;
  162. jose_io_auto_t *b64 = NULL;
  163. jose_io_auto_t *enc = NULL;
  164. json_auto_t *cfg = NULL;
  165. json_auto_t *jwk = NULL;
  166. json_auto_t *jwe = NULL;
  167. json_auto_t *sss = NULL;
  168. const char *key = NULL;
  169. const char *prt = NULL;
  170. const char *tag = NULL;
  171. const char *iv = NULL;
  172. json_t *pins = NULL;
  173. json_int_t t = 1;
  174. if (argc == 2 && strcmp(argv[1], "--summary") == 0) {
  175. fprintf(stdout, "%s\n", SUMMARY);
  176. return EXIT_SUCCESS;
  177. }
  178. if (isatty(STDIN_FILENO) || argc != 2)
  179. goto usage;
  180. /* Parse configuration. */
  181. cfg = json_loads(argv[1], 0, NULL);
  182. if (!cfg) {
  183. fprintf(stderr, "Error parsing config!\n");
  184. return EXIT_FAILURE;
  185. }
  186. if (json_unpack(cfg, "{s?I,s:o}", "t", &t, "pins", &pins) != 0) {
  187. fprintf(stderr, "Config missing 'pins' attribute!\n");
  188. return EXIT_FAILURE;
  189. }
  190. if (t < 1 || t > npins(pins)) {
  191. fprintf(stderr, "Invalid threshold (required: 1 <= %lld <= %lld)!\n",
  192. t, npins(pins));
  193. return EXIT_FAILURE;
  194. }
  195. sss = encrypt_frags(t, pins);
  196. if (!sss)
  197. return EXIT_FAILURE;
  198. /* Perform encryption using the key. */
  199. if (json_unpack(sss, "{s:[s]}", "e", &key) != 0)
  200. return EXIT_FAILURE;
  201. jwk = json_pack("{s:s,s:s,s:s}", "kty", "oct", "k", key, "alg", "A256GCM");
  202. if (!jwk)
  203. return EXIT_FAILURE;
  204. if (json_object_del(sss, "e") != 0)
  205. return EXIT_FAILURE;
  206. jwe = json_pack("{s:{s:s,s:{s:s,s:O}}}", "protected", "alg", "dir",
  207. "clevis", "pin", "sss", "sss", sss);
  208. if (!jwe)
  209. return EXIT_FAILURE;
  210. out = jose_io_file(NULL, stdout);
  211. b64 = jose_b64_enc_io(out);
  212. enc = jose_jwe_enc_cek_io(NULL, jwe, jwk, b64);
  213. if (!out || !b64 || !enc)
  214. return EXIT_FAILURE;
  215. if (json_unpack(jwe, "{s:s,s:s}", "protected", &prt, "iv", &iv) != 0)
  216. return EXIT_FAILURE;
  217. if (fprintf(stdout, "%s..%s.", prt, iv) < 0)
  218. return EXIT_FAILURE;
  219. while (!feof(stdin)) {
  220. uint8_t rd[1024] = {};
  221. size_t r = 0;
  222. r = fread(rd, 1, sizeof(rd), stdin);
  223. if (ferror(stdin)) {
  224. fprintf(stderr, "Error reading plaintext!\n");
  225. return EXIT_FAILURE;
  226. }
  227. if (!enc->feed(enc, rd, r))
  228. return EXIT_FAILURE;
  229. }
  230. if (!enc->done(enc))
  231. return EXIT_FAILURE;
  232. if (json_unpack(jwe, "{s:s}", "tag", &tag) != 0)
  233. return EXIT_FAILURE;
  234. if (fprintf(stdout, ".%s%s", tag, isatty(STDOUT_FILENO) ? "\n" : "") < 0)
  235. return EXIT_FAILURE;
  236. return EXIT_SUCCESS;
  237. usage:
  238. fprintf(stderr, "\n");
  239. fprintf(stderr, "Usage: clevis encrypt sss CONFIG < PLAINTEXT > JWE\n");
  240. fprintf(stderr, "\n");
  241. fprintf(stderr, "%s\n", SUMMARY);
  242. fprintf(stderr, "\n");
  243. fprintf(stderr, "This command uses the following configuration properties:\n");
  244. fprintf(stderr, "\n");
  245. fprintf(stderr, " t: <integer> Number of pins required for decryption (REQUIRED)\n");
  246. fprintf(stderr, "\n");
  247. fprintf(stderr, " pins: <object> Pins used for encrypting fragments (REQUIRED)\n");
  248. fprintf(stderr, "\n");
  249. fprintf(stderr, "Here is an example configuration for one of two servers:\n");
  250. fprintf(stderr, "\n");
  251. fprintf(stderr, "{\n");
  252. fprintf(stderr, " \"t\": 1,\n");
  253. fprintf(stderr, " \"pins\": {\n");
  254. fprintf(stderr, " \"tang\": [\n");
  255. fprintf(stderr, " { \"url\": \"http://example.com/tang1\" },\n");
  256. fprintf(stderr, " { \"url\": \"http://example.com/tang2\" }\n");
  257. fprintf(stderr, " ]\n");
  258. fprintf(stderr, " }\n");
  259. fprintf(stderr, "}\n");
  260. fprintf(stderr, "\n");
  261. return EXIT_FAILURE;
  262. }