main.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. /*-
  2. * Copyright 2009 Colin Percival
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  15. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  18. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24. * SUCH DAMAGE.
  25. */
  26. #include "platform.h"
  27. #include <errno.h>
  28. #include <math.h>
  29. #include <stdint.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include "getopt.h"
  34. #include "humansize.h"
  35. #include "insecure_memzero.h"
  36. #include "parsenum.h"
  37. #include "readpass.h"
  38. #include "scryptenc.h"
  39. #include "warnp.h"
  40. /* How should we get the passphrase? */
  41. enum passphrase_entry {
  42. PASSPHRASE_UNSET,
  43. PASSPHRASE_TTY_STDIN,
  44. PASSPHRASE_STDIN_ONCE,
  45. PASSPHRASE_TTY_ONCE,
  46. PASSPHRASE_ENV,
  47. PASSPHRASE_FILE,
  48. };
  49. static void
  50. usage(void)
  51. {
  52. fprintf(stderr,
  53. "usage: scrypt {enc | dec | info} [-f] [--logN value] [-M maxmem]\n"
  54. " [-m maxmemfrac] [-P] [-p value] [-r value]"
  55. " [-t maxtime] [-v]\n"
  56. " [--passphrase method:arg] infile [outfile]\n"
  57. " scrypt --version\n");
  58. exit(1);
  59. }
  60. /* Parse a numeric optarg within a GETOPT context. (Requires ch and optarg.) */
  61. #define GETOPT_PARSENUM_WITHIN_UNSIGNED(var, min, max) do { \
  62. if (PARSENUM((var), optarg, (min), (max))) { \
  63. if (errno == ERANGE) { \
  64. warn0("%s must be between %ju and %ju" \
  65. " (inclusive)", ch, (uintmax_t)(min), \
  66. (uintmax_t)(max)); \
  67. } else \
  68. warnp("Invalid option: %s %s", ch, optarg); \
  69. exit(1); \
  70. } \
  71. } while(0)
  72. static int
  73. parse_passphrase_arg(const char * arg,
  74. enum passphrase_entry * passphrase_entry_p, const char ** passphrase_arg_p)
  75. {
  76. const char * p;
  77. /* Find the separator in "method:arg", or fail if there isn't one. */
  78. if ((p = strchr(arg, ':')) == NULL)
  79. goto err1;
  80. /* Extract the "arg" part. */
  81. *passphrase_arg_p = &p[1];
  82. /* Parse the "method". */
  83. if (strncmp(arg, "dev:", 4) == 0) {
  84. if (strcmp(*passphrase_arg_p, "tty-stdin") == 0) {
  85. *passphrase_entry_p = PASSPHRASE_TTY_STDIN;
  86. goto success;
  87. }
  88. else if (strcmp(*passphrase_arg_p, "stdin-once") == 0) {
  89. *passphrase_entry_p = PASSPHRASE_STDIN_ONCE;
  90. goto success;
  91. }
  92. else if (strcmp(*passphrase_arg_p, "tty-once") == 0) {
  93. *passphrase_entry_p = PASSPHRASE_TTY_ONCE;
  94. goto success;
  95. }
  96. }
  97. if (strncmp(optarg, "env:", 4) == 0) {
  98. *passphrase_entry_p = PASSPHRASE_ENV;
  99. goto success;
  100. }
  101. if (strncmp(optarg, "file:", 5) == 0) {
  102. *passphrase_entry_p = PASSPHRASE_FILE;
  103. goto success;
  104. }
  105. err1:
  106. warn0("Invalid option: --passphrase %s", arg);
  107. /* Failure! */
  108. return (-1);
  109. success:
  110. /* Success! */
  111. return (0);
  112. }
  113. int
  114. main(int argc, char *argv[])
  115. {
  116. FILE * infile;
  117. FILE * outfile = stdout;
  118. int dec = 0;
  119. int info = 0;
  120. int force_resources = 0;
  121. uint64_t maxmem64;
  122. struct scryptenc_params params = {0, 0.5, 300.0, 0, 0, 0};
  123. const char * ch;
  124. const char * infilename;
  125. const char * outfilename;
  126. char * passwd;
  127. int rc;
  128. int verbose = 0;
  129. struct scryptdec_file_cookie * C = NULL;
  130. enum passphrase_entry passphrase_entry = PASSPHRASE_UNSET;
  131. const char * passphrase_arg;
  132. const char * passwd_env;
  133. WARNP_INIT;
  134. /* We should have "enc", "dec", or "info" first. */
  135. if (argc < 2)
  136. usage();
  137. if (strcmp(argv[1], "enc") == 0) {
  138. params.maxmem = 0;
  139. params.maxmemfrac = 0.125;
  140. params.maxtime = 5.0;
  141. } else if (strcmp(argv[1], "dec") == 0) {
  142. dec = 1;
  143. } else if (strcmp(argv[1], "info") == 0) {
  144. info = 1;
  145. } else if (strcmp(argv[1], "--version") == 0) {
  146. fprintf(stdout, "scrypt %s\n", PACKAGE_VERSION);
  147. exit(0);
  148. } else {
  149. warn0("First argument must be 'enc', 'dec', or 'info'.");
  150. usage();
  151. }
  152. argc--;
  153. argv++;
  154. /* Parse arguments. */
  155. while ((ch = GETOPT(argc, argv)) != NULL) {
  156. GETOPT_SWITCH(ch) {
  157. GETOPT_OPT("-f"):
  158. force_resources = 1;
  159. break;
  160. GETOPT_OPTARG("--logN"):
  161. GETOPT_PARSENUM_WITHIN_UNSIGNED(&params.logN, 10, 40);
  162. break;
  163. GETOPT_OPTARG("-M"):
  164. if (humansize_parse(optarg, &maxmem64)) {
  165. warn0("Could not parse the parameter to -M.");
  166. exit(1);
  167. }
  168. if (maxmem64 > SIZE_MAX) {
  169. warn0("The parameter to -M is too large.");
  170. exit(1);
  171. }
  172. params.maxmem = (size_t)maxmem64;
  173. break;
  174. GETOPT_OPTARG("-m"):
  175. if (PARSENUM(&params.maxmemfrac, optarg, 0, 1)) {
  176. warnp("Invalid option: -m %s", optarg);
  177. exit(1);
  178. }
  179. break;
  180. GETOPT_OPTARG("-p"):
  181. GETOPT_PARSENUM_WITHIN_UNSIGNED(&params.p, 1, 32);
  182. break;
  183. GETOPT_OPTARG("--passphrase"):
  184. if (passphrase_entry != PASSPHRASE_UNSET) {
  185. warn0("You can only enter one --passphrase or"
  186. " -P argument");
  187. exit(1);
  188. }
  189. /* Parse "method:arg" optarg. */
  190. if (parse_passphrase_arg(optarg, &passphrase_entry,
  191. &passphrase_arg))
  192. exit(1);
  193. break;
  194. GETOPT_OPTARG("-r"):
  195. GETOPT_PARSENUM_WITHIN_UNSIGNED(&params.r, 1, 32);
  196. break;
  197. GETOPT_OPTARG("-t"):
  198. if (PARSENUM(&params.maxtime, optarg, 0, INFINITY)) {
  199. warnp("Invalid option: -t %s", optarg);
  200. exit(1);
  201. }
  202. break;
  203. GETOPT_OPT("-v"):
  204. verbose = 1;
  205. break;
  206. GETOPT_OPT("-P"):
  207. if (passphrase_entry != PASSPHRASE_UNSET) {
  208. warn0("You can only enter one --passphrase or"
  209. " -P argument");
  210. exit(1);
  211. }
  212. passphrase_entry = PASSPHRASE_STDIN_ONCE;
  213. break;
  214. GETOPT_MISSING_ARG:
  215. warn0("Missing argument to %s", ch);
  216. usage();
  217. GETOPT_DEFAULT:
  218. warn0("illegal option -- %s", ch);
  219. usage();
  220. }
  221. }
  222. argc -= optind;
  223. argv += optind;
  224. /* We must have one or two parameters left. */
  225. if ((argc < 1) || (argc > 2))
  226. usage();
  227. /* The explicit parameters must be zero, or all non-zero. */
  228. if ((params.logN != 0) && ((params.r == 0) || (params.p == 0))) {
  229. warn0("If --logN is set, -r and -p must also be set");
  230. goto err0;
  231. }
  232. if ((params.r != 0) && ((params.logN == 0) || (params.p == 0))) {
  233. warn0("If -r is set, --logN and -p must also be set");
  234. goto err0;
  235. }
  236. if ((params.p != 0) && ((params.logN == 0) || (params.r == 0))) {
  237. warn0("If -p is set, --logN and -r must also be set");
  238. goto err0;
  239. }
  240. /* Set the input filename. */
  241. if (strcmp(argv[0], "-"))
  242. infilename = argv[0];
  243. else
  244. infilename = NULL;
  245. /* Set the output filename. */
  246. if (argc > 1)
  247. outfilename = argv[1];
  248. else
  249. outfilename = NULL;
  250. /* Set the default passphrase entry method. */
  251. if (passphrase_entry == PASSPHRASE_UNSET)
  252. passphrase_entry = PASSPHRASE_TTY_STDIN;
  253. /* If the input isn't stdin, open the file. */
  254. if (infilename != NULL) {
  255. if ((infile = fopen(infilename, "rb")) == NULL) {
  256. warnp("Cannot open input file: %s", infilename);
  257. goto err0;
  258. }
  259. } else {
  260. infile = stdin;
  261. /* Error if given incompatible options. */
  262. if (passphrase_entry == PASSPHRASE_STDIN_ONCE) {
  263. warn0("Cannot read both passphrase and input file"
  264. " from standard input");
  265. goto err0;
  266. }
  267. }
  268. /* User selected 'info' mode. */
  269. if (info) {
  270. /* Print the encryption parameters used for the file. */
  271. rc = scryptdec_file_printparams(infile);
  272. /* Clean up. */
  273. if (infile != stdin)
  274. fclose(infile);
  275. /* Finished! */
  276. goto done;
  277. }
  278. /* Get the password. */
  279. switch (passphrase_entry) {
  280. case PASSPHRASE_TTY_STDIN:
  281. /* Read passphrase, prompting only once if decrypting. */
  282. if (readpass(&passwd, "Please enter passphrase",
  283. (dec) ? NULL : "Please confirm passphrase", 1))
  284. goto err1;
  285. break;
  286. case PASSPHRASE_STDIN_ONCE:
  287. /* Read passphrase, prompting only once, from stdin only. */
  288. if (readpass(&passwd, "Please enter passphrase", NULL, 0))
  289. goto err1;
  290. break;
  291. case PASSPHRASE_TTY_ONCE:
  292. /* Read passphrase, prompting only once, from tty only. */
  293. if (readpass(&passwd, "Please enter passphrase", NULL, 2))
  294. goto err1;
  295. break;
  296. case PASSPHRASE_ENV:
  297. /* We're not allowed to modify the output of getenv(). */
  298. if ((passwd_env = getenv(passphrase_arg)) == NULL) {
  299. warn0("Failed to read from ${%s}", passphrase_arg);
  300. goto err1;
  301. }
  302. /* This allows us to use the same insecure_zero() logic. */
  303. if ((passwd = strdup(passwd_env)) == NULL) {
  304. warnp("Out of memory");
  305. goto err1;
  306. }
  307. break;
  308. case PASSPHRASE_FILE:
  309. if (readpass_file(&passwd, passphrase_arg))
  310. goto err1;
  311. break;
  312. case PASSPHRASE_UNSET:
  313. warn0("Programming error: passphrase_entry is not set");
  314. goto err1;
  315. }
  316. /*-
  317. * If we're decrypting, open the input file and process its header;
  318. * doing this here allows us to abort without creating an output
  319. * file if the input file does not have a valid scrypt header or if
  320. * we have the wrong passphrase.
  321. *
  322. * If successful, we get back a cookie containing the decryption
  323. * parameters (which we'll use after we open the output file).
  324. */
  325. if (dec) {
  326. if ((rc = scryptdec_file_prep(infile, (uint8_t *)passwd,
  327. strlen(passwd), &params, verbose, force_resources,
  328. &C)) != 0) {
  329. goto cleanup;
  330. }
  331. }
  332. /* If we have an output file, open it. */
  333. if (outfilename != NULL) {
  334. if ((outfile = fopen(outfilename, "wb")) == NULL) {
  335. warnp("Cannot open output file: %s", outfilename);
  336. goto err2;
  337. }
  338. }
  339. /* Encrypt or decrypt. */
  340. if (dec)
  341. rc = scryptdec_file_copy(C, outfile);
  342. else
  343. rc = scryptenc_file(infile, outfile, (uint8_t *)passwd,
  344. strlen(passwd), &params, verbose, force_resources);
  345. cleanup:
  346. /* Free the decryption cookie, if any. */
  347. scryptdec_file_cookie_free(C);
  348. /* Zero and free the password. */
  349. insecure_memzero(passwd, strlen(passwd));
  350. free(passwd);
  351. /* Close any files we opened. */
  352. if (infile != stdin)
  353. fclose(infile);
  354. if (outfile != stdout)
  355. fclose(outfile);
  356. done:
  357. /* If we failed, print the right error message and exit. */
  358. if (rc != SCRYPT_OK) {
  359. switch (rc) {
  360. case SCRYPT_ELIMIT:
  361. warnp("Error determining amount of available memory");
  362. break;
  363. case SCRYPT_ECLOCK:
  364. warnp("Error reading clocks");
  365. break;
  366. case SCRYPT_EKEY:
  367. warnp("Error computing derived key");
  368. break;
  369. case SCRYPT_ESALT:
  370. warnp("Error reading salt");
  371. break;
  372. case SCRYPT_EOPENSSL:
  373. warnp("OpenSSL error");
  374. break;
  375. case SCRYPT_ENOMEM:
  376. warnp("Error allocating memory");
  377. break;
  378. case SCRYPT_EINVAL:
  379. warn0("Input is not valid scrypt-encrypted block");
  380. break;
  381. case SCRYPT_EVERSION:
  382. warn0("Unrecognized scrypt format version");
  383. break;
  384. case SCRYPT_ETOOBIG:
  385. warn0("Decrypting file would require too much memory");
  386. break;
  387. case SCRYPT_ETOOSLOW:
  388. warn0("Decrypting file would take too much CPU time");
  389. break;
  390. case SCRYPT_EPASS:
  391. warn0("Passphrase is incorrect");
  392. break;
  393. case SCRYPT_EWRFILE:
  394. warnp("Error writing file: %s",
  395. (outfilename != NULL) ? outfilename
  396. : "standard output");
  397. break;
  398. case SCRYPT_ERDFILE:
  399. warnp("Error reading file: %s",
  400. (infilename != NULL) ? infilename
  401. : "standard input");
  402. break;
  403. case SCRYPT_EPARAM:
  404. warn0("Error in explicit parameters");
  405. break;
  406. }
  407. goto err0;
  408. }
  409. /* Success! */
  410. return (0);
  411. err2:
  412. scryptdec_file_cookie_free(C);
  413. insecure_memzero(passwd, strlen(passwd));
  414. free(passwd);
  415. err1:
  416. if (infile != stdin)
  417. fclose(infile);
  418. err0:
  419. /* Failure! */
  420. exit(1);
  421. }