main.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /*
  2. * usign - tiny signify replacement
  3. *
  4. * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
  5. *
  6. * Permission to use, copy, modify, and/or distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. #include <sys/mman.h>
  19. #include <sys/stat.h>
  20. #include <stdio.h>
  21. #include <stdbool.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <getopt.h>
  25. #include <stdint.h>
  26. #include <fcntl.h>
  27. #include <unistd.h>
  28. #include <inttypes.h>
  29. #include "base64.h"
  30. #include "edsign.h"
  31. #include "ed25519.h"
  32. struct pubkey {
  33. char pkalg[2];
  34. uint8_t fingerprint[8];
  35. uint8_t pubkey[EDSIGN_PUBLIC_KEY_SIZE];
  36. };
  37. struct seckey {
  38. char pkalg[2];
  39. char kdfalg[2];
  40. uint32_t kdfrounds;
  41. uint8_t salt[16];
  42. uint8_t checksum[8];
  43. uint8_t fingerprint[8];
  44. uint8_t seckey[64];
  45. };
  46. struct sig {
  47. char pkalg[2];
  48. uint8_t fingerprint[8];
  49. uint8_t sig[EDSIGN_SIGNATURE_SIZE];
  50. };
  51. static const char *pubkeyfile;
  52. static const char *pubkeydir;
  53. static const char *sigfile;
  54. static const char *seckeyfile;
  55. static bool quiet;
  56. static enum {
  57. CMD_NONE,
  58. CMD_VERIFY,
  59. CMD_SIGN,
  60. CMD_FINGERPRINT,
  61. CMD_GENERATE,
  62. } cmd = CMD_NONE;
  63. static uint64_t fingerprint_u64(uint8_t *data)
  64. {
  65. uint64_t val = 0;
  66. #define ADD(_v) val = (val << 8) | _v
  67. ADD(data[0]);
  68. ADD(data[1]);
  69. ADD(data[2]);
  70. ADD(data[3]);
  71. ADD(data[4]);
  72. ADD(data[5]);
  73. ADD(data[6]);
  74. ADD(data[7]);
  75. #undef ADD
  76. return val;
  77. }
  78. static void
  79. file_error(const char *filename, bool _read)
  80. {
  81. if (!quiet || cmd != CMD_VERIFY)
  82. fprintf(stderr, "Cannot open file '%s' for %s\n", filename,
  83. _read ? "reading" : "writing");
  84. exit(1);
  85. }
  86. static FILE *
  87. open_file(const char *filename, bool _read)
  88. {
  89. FILE *f;
  90. if (!strcmp(filename, "-"))
  91. return _read ? stdin : stdout;
  92. f = fopen(filename, _read ? "r" : "w");
  93. if (!f)
  94. file_error(filename, _read);
  95. return f;
  96. }
  97. static void
  98. get_file(const char *filename, char *buf, int buflen)
  99. {
  100. FILE *f = open_file(filename, true);
  101. int len;
  102. while (1) {
  103. char *cur = fgets(buf, buflen, f);
  104. if (!cur) {
  105. fprintf(stderr, "Premature end of file\n");
  106. exit(1);
  107. }
  108. if (strchr(buf, '\n'))
  109. break;
  110. }
  111. len = fread(buf, 1, buflen - 1, f);
  112. buf[len] = 0;
  113. }
  114. static bool
  115. get_base64_file(const char *file, void *dest, int size, void *buf, int buflen)
  116. {
  117. get_file(file, buf, buflen - 1);
  118. return b64_pton(buf, dest, size) == size;
  119. }
  120. static int verify(const char *msgfile)
  121. {
  122. struct pubkey pkey;
  123. struct sig sig;
  124. struct edsign_verify_state vst;
  125. FILE *f;
  126. char buf[512];
  127. f = open_file(msgfile, true);
  128. if (!f) {
  129. fprintf(stderr, "Cannot open message file\n");
  130. return 1;
  131. }
  132. if (!get_base64_file(sigfile, &sig, sizeof(sig), buf, sizeof(buf)) ||
  133. memcmp(sig.pkalg, "Ed", 2) != 0) {
  134. fprintf(stderr, "Failed to decode signature\n");
  135. return 1;
  136. }
  137. if (!pubkeyfile) {
  138. snprintf(buf, sizeof(buf), "%s/%"PRIx64, pubkeydir,
  139. fingerprint_u64(sig.fingerprint));
  140. pubkeyfile = buf;
  141. }
  142. if (!get_base64_file(pubkeyfile, &pkey, sizeof(pkey), buf, sizeof(buf)) ||
  143. memcmp(pkey.pkalg, "Ed", 2) != 0) {
  144. fprintf(stderr, "Failed to decode public key\n");
  145. return 1;
  146. }
  147. edsign_verify_init(&vst, sig.sig, pkey.pubkey);
  148. while (!feof(f)) {
  149. int len = fread(buf, 1, sizeof(buf), f);
  150. edsign_verify_add(&vst, buf, len);
  151. }
  152. fclose(f);
  153. if (!edsign_verify(&vst, sig.sig, pkey.pubkey)) {
  154. if (!quiet)
  155. fprintf(stderr, "verification failed\n");
  156. return 1;
  157. }
  158. if (!quiet)
  159. fprintf(stderr, "OK\n");
  160. return 0;
  161. }
  162. static int sign(const char *msgfile)
  163. {
  164. struct seckey skey;
  165. struct sig sig = {
  166. .pkalg = "Ed",
  167. };
  168. struct stat st;
  169. char buf[512];
  170. void *pubkey = buf;
  171. long mlen;
  172. void *m;
  173. int mfd;
  174. FILE *out;
  175. if (!get_base64_file(seckeyfile, &skey, sizeof(skey), buf, sizeof(buf)) ||
  176. memcmp(skey.pkalg, "Ed", 2) != 0) {
  177. fprintf(stderr, "Failed to decode secret key\n");
  178. return 1;
  179. }
  180. if (skey.kdfrounds) {
  181. fprintf(stderr, "Password protected secret keys are not supported\n");
  182. return 1;
  183. }
  184. mfd = open(msgfile, O_RDONLY, 0);
  185. if (mfd < 0 || fstat(mfd, &st) < 0 ||
  186. (m = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, mfd, 0)) == MAP_FAILED) {
  187. if (mfd >= 0)
  188. close(mfd);
  189. perror("Cannot open message file");
  190. return 1;
  191. }
  192. mlen = st.st_size;
  193. memcpy(sig.fingerprint, skey.fingerprint, sizeof(sig.fingerprint));
  194. edsign_sec_to_pub(pubkey, skey.seckey);
  195. edsign_sign(sig.sig, pubkey, skey.seckey, m, mlen);
  196. munmap(m, mlen);
  197. close(mfd);
  198. if (b64_ntop(&sig, sizeof(sig), buf, sizeof(buf)) < 0)
  199. return 1;
  200. out = open_file(sigfile, false);
  201. fprintf(out, "untrusted comment: signed by key %"PRIx64"\n%s\n", fingerprint_u64(sig.fingerprint), buf);
  202. fclose(out);
  203. return 0;
  204. }
  205. static int fingerprint(void)
  206. {
  207. struct seckey skey;
  208. struct pubkey pkey;
  209. struct sig sig;
  210. char buf[512];
  211. uint8_t *fp;
  212. if (seckeyfile &&
  213. get_base64_file(seckeyfile, &skey, sizeof(skey), buf, sizeof(buf)))
  214. fp = skey.fingerprint;
  215. else if (pubkeyfile &&
  216. get_base64_file(pubkeyfile, &pkey, sizeof(pkey), buf, sizeof(buf)))
  217. fp = pkey.fingerprint;
  218. else if (sigfile &&
  219. get_base64_file(sigfile, &sig, sizeof(sig), buf, sizeof(buf)))
  220. fp = sig.fingerprint;
  221. else
  222. return 1;
  223. fprintf(stdout, "%"PRIx64"\n", fingerprint_u64(fp));
  224. return 0;
  225. }
  226. static int generate(void)
  227. {
  228. struct seckey skey = {
  229. .pkalg = "Ed",
  230. .kdfalg = "BK",
  231. .kdfrounds = 0,
  232. };
  233. struct pubkey pkey = {
  234. .pkalg = "Ed",
  235. };
  236. struct sha512_state s;
  237. char buf[512];
  238. FILE *f;
  239. f = fopen("/dev/urandom", "r");
  240. if (!f ||
  241. fread(skey.fingerprint, sizeof(skey.fingerprint), 1, f) != 1 ||
  242. fread(skey.seckey, EDSIGN_SECRET_KEY_SIZE, 1, f) != 1 ||
  243. fread(skey.salt, sizeof(skey.salt), 1, f) != 1) {
  244. fprintf(stderr, "Can't read data from /dev/urandom\n");
  245. return 1;
  246. }
  247. if (f)
  248. fclose(f);
  249. ed25519_prepare(skey.seckey);
  250. edsign_sec_to_pub(skey.seckey + 32, skey.seckey);
  251. sha512_init(&s);
  252. sha512_add(&s, skey.seckey, sizeof(skey.seckey));
  253. memcpy(skey.checksum, sha512_final_get(&s), sizeof(skey.checksum));
  254. if (b64_ntop(&skey, sizeof(skey), buf, sizeof(buf)) < 0)
  255. return 1;
  256. f = open_file(seckeyfile, false);
  257. fprintf(f, "untrusted comment: secret key %"PRIx64"\n%s\n", fingerprint_u64(skey.fingerprint), buf);
  258. fclose(f);
  259. memcpy(pkey.fingerprint, skey.fingerprint, sizeof(pkey.fingerprint));
  260. memcpy(pkey.pubkey, skey.seckey + 32, sizeof(pkey.pubkey));
  261. if (b64_ntop(&pkey, sizeof(pkey), buf, sizeof(buf)) < 0)
  262. return 1;
  263. f = open_file(pubkeyfile, false);
  264. fprintf(f, "untrusted comment: public key %"PRIx64"\n%s\n", fingerprint_u64(pkey.fingerprint), buf);
  265. fclose(f);
  266. return 0;
  267. }
  268. static int usage(const char *cmd)
  269. {
  270. fprintf(stderr,
  271. "Usage: %s <command> <options>\n"
  272. "Commands:\n"
  273. " -V: verify (needs at least -m and -p|-P)\n"
  274. " -S: sign (needs at least -m and -s)\n"
  275. " -F: print key fingerprint of public/secret key or signature\n"
  276. " -G: generate a new keypair\n"
  277. "Options:\n"
  278. " -m <file>: message file\n"
  279. " -p <file>: public key file (verify/fingerprint only)\n"
  280. " -P <path>: public key directory (verify only)\n"
  281. " -q: quiet (do not print verification result, use return code only)\n"
  282. " -s <file>: secret key file (sign/fingerprint only)\n"
  283. " -x <file>: signature file (defaults to <message file>.sig)\n"
  284. "\n",
  285. cmd);
  286. return 1;
  287. }
  288. static void set_cmd(const char *prog, int val)
  289. {
  290. if (cmd != CMD_NONE)
  291. exit(usage(prog));
  292. cmd = val;
  293. }
  294. int main(int argc, char **argv)
  295. {
  296. const char *msgfile = NULL;
  297. int ch;
  298. while ((ch = getopt(argc, argv, "FGSVm:P:p:qs:x:")) != -1) {
  299. switch (ch) {
  300. case 'V':
  301. set_cmd(argv[0], CMD_VERIFY);
  302. break;
  303. case 'S':
  304. set_cmd(argv[0], CMD_SIGN);
  305. break;
  306. case 'F':
  307. set_cmd(argv[0], CMD_FINGERPRINT);
  308. break;
  309. case 'G':
  310. set_cmd(argv[0], CMD_GENERATE);
  311. break;
  312. case 'm':
  313. msgfile = optarg;
  314. break;
  315. case 'P':
  316. pubkeydir = optarg;
  317. break;
  318. case 'p':
  319. pubkeyfile = optarg;
  320. break;
  321. case 's':
  322. seckeyfile = optarg;
  323. break;
  324. case 'x':
  325. sigfile = optarg;
  326. break;
  327. case 'q':
  328. quiet = true;
  329. break;
  330. default:
  331. return usage(argv[0]);
  332. }
  333. }
  334. if (!sigfile && msgfile) {
  335. char *buf = alloca(strlen(msgfile) + 5);
  336. if (!strcmp(msgfile, "-")) {
  337. fprintf(stderr, "Need signature file when reading message from stdin\n");
  338. return 1;
  339. }
  340. sprintf(buf, "%s.sig", msgfile);
  341. sigfile = buf;
  342. }
  343. switch (cmd) {
  344. case CMD_VERIFY:
  345. if ((!pubkeyfile && !pubkeydir) || !msgfile)
  346. return usage(argv[0]);
  347. return verify(msgfile);
  348. case CMD_SIGN:
  349. if (!seckeyfile || !msgfile || !sigfile)
  350. return usage(argv[0]);
  351. return sign(msgfile);
  352. case CMD_FINGERPRINT:
  353. if (!!seckeyfile + !!pubkeyfile + !!sigfile != 1) {
  354. fprintf(stderr, "Need one secret/public key or signature\n");
  355. return usage(argv[0]);
  356. }
  357. return fingerprint();
  358. case CMD_GENERATE:
  359. if (!seckeyfile || !pubkeyfile)
  360. return usage(argv[0]);
  361. return generate();
  362. default:
  363. return usage(argv[0]);
  364. }
  365. }