clevis-encrypt-sss.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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, int assume_yes)
  79. {
  80. char *args[] = { "clevis", "encrypt", (char *) pin, NULL, 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. int status = 0;
  88. str = args[3] = json_dumps(cfg, JSON_SORT_KEYS | JSON_COMPACT);
  89. if (!str)
  90. return NULL;
  91. if (assume_yes) {
  92. args[4] = "-y";
  93. }
  94. pnt = sss_point(sss, &pntl);
  95. if (!pnt)
  96. return NULL;
  97. pipe = call(args, pnt, pntl, &pid);
  98. OPENSSL_cleanse(pnt, pntl);
  99. free(pnt);
  100. if (!pipe)
  101. return NULL;
  102. while (!feof(pipe)) {
  103. char buf[1024] = {};
  104. json_t *tmp = NULL;
  105. size_t rd = 0;
  106. rd = fread(buf, 1, sizeof(buf), pipe);
  107. if (ferror(pipe)) {
  108. fclose(pipe);
  109. return NULL;
  110. }
  111. tmp = json_pack("s+%", json_string_value(jwe), buf, rd);
  112. if (!tmp) {
  113. fclose(pipe);
  114. return NULL;
  115. }
  116. json_decref(jwe);
  117. jwe = tmp;
  118. }
  119. fclose(pipe);
  120. waitpid(pid, &status, 0);
  121. if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
  122. return NULL;
  123. }
  124. return json_incref(jwe);
  125. }
  126. static json_t *
  127. encrypt_frags(json_int_t t, json_t *pins, int assume_yes)
  128. {
  129. const char *pname = NULL;
  130. json_auto_t *sss = NULL;
  131. json_t *pcfgs = NULL;
  132. json_t *parr = NULL;
  133. /* Generate the SSS polynomial. */
  134. sss = sss_generate(32, t);
  135. if (!sss) {
  136. fprintf(stderr, "Error generating SSS!\n");
  137. return NULL;
  138. }
  139. if (json_object_set_new(sss, "jwe", parr = json_array()) < 0)
  140. return NULL;
  141. /* Encrypt each key share with a child pin. */
  142. json_object_foreach(pins, pname, pcfgs) {
  143. json_t *pcfg = NULL;
  144. size_t i = 0;
  145. if (json_is_object(pcfgs))
  146. pcfgs = json_pack("[O]", pcfgs);
  147. else if (json_is_array(pcfgs))
  148. pcfgs = json_incref(pcfgs);
  149. else
  150. return NULL;
  151. if (json_object_set_new(pins, pname, pcfgs) < 0)
  152. return NULL;
  153. json_array_foreach(pcfgs, i, pcfg) {
  154. json_auto_t *jwe = NULL;
  155. jwe = encrypt_frag(sss, pname, pcfg, assume_yes);
  156. if (!jwe)
  157. return NULL;
  158. if (json_array_append(parr, jwe) < 0)
  159. return NULL;
  160. }
  161. }
  162. return json_incref(sss);
  163. }
  164. int
  165. main(int argc, char *argv[])
  166. {
  167. const char *SUMMARY = "Encrypts using a Shamir's Secret Sharing policy";
  168. jose_io_auto_t *out = NULL;
  169. jose_io_auto_t *b64 = NULL;
  170. jose_io_auto_t *enc = NULL;
  171. json_auto_t *cfg = NULL;
  172. json_auto_t *jwk = NULL;
  173. json_auto_t *jwe = NULL;
  174. json_auto_t *sss = NULL;
  175. const char *key = NULL;
  176. const char *prt = NULL;
  177. const char *tag = NULL;
  178. const char *iv = NULL;
  179. json_t *pins = NULL;
  180. json_int_t t = 1;
  181. int assume_yes = 0;
  182. if (argc == 2 && strcmp(argv[1], "--summary") == 0) {
  183. fprintf(stdout, "%s\n", SUMMARY);
  184. return EXIT_SUCCESS;
  185. }
  186. if (isatty(STDIN_FILENO) || argc != 2) {
  187. if (argc != 3) {
  188. goto usage;
  189. }
  190. if (strcmp(argv[2], "-y") == 0) {
  191. assume_yes = 1;
  192. } else if (strlen(argv[2]) > 0) {
  193. goto usage;
  194. }
  195. }
  196. /* Parse configuration. */
  197. cfg = json_loads(argv[1], 0, NULL);
  198. if (!cfg) {
  199. fprintf(stderr, "Error parsing config!\n");
  200. return EXIT_FAILURE;
  201. }
  202. if (json_unpack(cfg, "{s?I,s:o}", "t", &t, "pins", &pins) != 0) {
  203. fprintf(stderr, "Config missing 'pins' attribute!\n");
  204. return EXIT_FAILURE;
  205. }
  206. if (t < 1 || t > npins(pins)) {
  207. fprintf(stderr, "Invalid threshold (required: 1 <= %lld <= %lld)!\n",
  208. t, npins(pins));
  209. return EXIT_FAILURE;
  210. }
  211. sss = encrypt_frags(t, pins, assume_yes);
  212. if (!sss)
  213. return EXIT_FAILURE;
  214. /* Perform encryption using the key. */
  215. if (json_unpack(sss, "{s:[s]}", "e", &key) != 0)
  216. return EXIT_FAILURE;
  217. jwk = json_pack("{s:s,s:s,s:s}", "kty", "oct", "k", key, "alg", "A256GCM");
  218. if (!jwk)
  219. return EXIT_FAILURE;
  220. if (json_object_del(sss, "e") != 0)
  221. return EXIT_FAILURE;
  222. jwe = json_pack("{s:{s:s,s:{s:s,s:O}}}", "protected", "alg", "dir",
  223. "clevis", "pin", "sss", "sss", sss);
  224. if (!jwe)
  225. return EXIT_FAILURE;
  226. out = jose_io_file(NULL, stdout);
  227. b64 = jose_b64_enc_io(out);
  228. enc = jose_jwe_enc_cek_io(NULL, jwe, jwk, b64);
  229. if (!out || !b64 || !enc)
  230. return EXIT_FAILURE;
  231. if (json_unpack(jwe, "{s:s,s:s}", "protected", &prt, "iv", &iv) != 0)
  232. return EXIT_FAILURE;
  233. if (fprintf(stdout, "%s..%s.", prt, iv) < 0)
  234. return EXIT_FAILURE;
  235. while (!feof(stdin)) {
  236. uint8_t rd[1024] = {};
  237. size_t r = 0;
  238. r = fread(rd, 1, sizeof(rd), stdin);
  239. if (ferror(stdin)) {
  240. fprintf(stderr, "Error reading plaintext!\n");
  241. return EXIT_FAILURE;
  242. }
  243. if (!enc->feed(enc, rd, r))
  244. return EXIT_FAILURE;
  245. }
  246. if (!enc->done(enc))
  247. return EXIT_FAILURE;
  248. if (json_unpack(jwe, "{s:s}", "tag", &tag) != 0)
  249. return EXIT_FAILURE;
  250. if (fprintf(stdout, ".%s%s", tag, isatty(STDOUT_FILENO) ? "\n" : "") < 0)
  251. return EXIT_FAILURE;
  252. return EXIT_SUCCESS;
  253. usage:
  254. fprintf(stderr, "\n");
  255. fprintf(stderr, "Usage: clevis encrypt sss CONFIG [-y] < PLAINTEXT > JWE\n");
  256. fprintf(stderr, "\n");
  257. fprintf(stderr, "%s\n", SUMMARY);
  258. fprintf(stderr, "\n");
  259. fprintf(stderr, "This command uses the following configuration properties:\n");
  260. fprintf(stderr, "\n");
  261. fprintf(stderr, " t: <integer> Number of pins required for decryption (REQUIRED)\n");
  262. fprintf(stderr, "\n");
  263. fprintf(stderr, " pins: <object> Pins used for encrypting fragments (REQUIRED)\n");
  264. fprintf(stderr, "\n");
  265. fprintf(stderr, "Here is an example configuration for one of two servers:\n");
  266. fprintf(stderr, "\n");
  267. fprintf(stderr, "{\n");
  268. fprintf(stderr, " \"t\": 1,\n");
  269. fprintf(stderr, " \"pins\": {\n");
  270. fprintf(stderr, " \"tang\": [\n");
  271. fprintf(stderr, " { \"url\": \"http://example.com/tang1\" },\n");
  272. fprintf(stderr, " { \"url\": \"http://example.com/tang2\" }\n");
  273. fprintf(stderr, " ]\n");
  274. fprintf(stderr, " }\n");
  275. fprintf(stderr, "}\n");
  276. fprintf(stderr, "\n");
  277. return EXIT_FAILURE;
  278. }