main.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  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 "passphrase_entry.h"
  38. #include "scryptenc.h"
  39. #include "scryptenc_print_error.h"
  40. #include "warnp.h"
  41. static void
  42. usage(void)
  43. {
  44. fprintf(stderr,
  45. "usage: scrypt {enc | dec | info} [-f] [--logN value] [-M maxmem]\n"
  46. " [-m maxmemfrac] [-P] [-p value] [-r value]"
  47. " [-t maxtime] [-v]\n"
  48. " [--passphrase method:arg] infile [outfile]\n"
  49. " scrypt --version\n");
  50. exit(1);
  51. }
  52. /**
  53. * scrypt_mode_info(infilename):
  54. * Print scrypt parameters used for the specified ${infilename}, or read from
  55. * stdin if that argument is NULL.
  56. */
  57. static int
  58. scrypt_mode_info(const char * infilename)
  59. {
  60. FILE * infile;
  61. int rc;
  62. /* If the input isn't stdin, open the file. */
  63. if (infilename != NULL) {
  64. if ((infile = fopen(infilename, "rb")) == NULL) {
  65. warnp("Cannot open input file: %s", infilename);
  66. goto err0;
  67. }
  68. } else {
  69. infile = stdin;
  70. }
  71. /* Print the encryption parameters used for the file. */
  72. if ((rc = scryptdec_file_printparams(infile)) != SCRYPT_OK) {
  73. scryptenc_print_error(rc, infilename, NULL);
  74. goto err1;
  75. }
  76. /* Clean up. */
  77. if ((infile != stdin) && fclose(infile))
  78. warnp("fclose");
  79. /* Success! */
  80. return (0);
  81. err1:
  82. if ((infile != stdin) && fclose(infile))
  83. warnp("fclose");
  84. err0:
  85. /* Failure! */
  86. return (-1);
  87. }
  88. /**
  89. * scrypt_mode_enc_dec(params, passphrase_entry, passphrase_arg, dec, verbose,
  90. * force_resources, infilename, outfilename):
  91. * Either encrypt (if ${dec} is 0) or decrypt (if ${dec} is non-zero)
  92. * ${infilename} (or standard input if this is NULL) to ${outfilename}.
  93. * Use scrypt parameters ${params}, with passphrase entry method
  94. * ${passphrase_entry} and argument ${passphrase_arg}. If ${verbose} is
  95. * non-zero, print verbose messages. If ${force_resources} is non-zero,
  96. * do not check whether encryption or decryption will exceed the estimated
  97. * time or memory requirements.
  98. */
  99. static int
  100. scrypt_mode_enc_dec(struct scryptenc_params params,
  101. enum passphrase_entry passphrase_entry, const char * passphrase_arg,
  102. int dec, int verbose, int force_resources,
  103. const char * infilename, const char * outfilename)
  104. {
  105. struct scryptdec_file_cookie * C = NULL;
  106. FILE * infile;
  107. FILE * outfile = stdout;
  108. char * passwd;
  109. int rc;
  110. /* If the input isn't stdin, open the file. */
  111. if (infilename != NULL) {
  112. if ((infile = fopen(infilename, "rb")) == NULL) {
  113. warnp("Cannot open input file: %s", infilename);
  114. goto err0;
  115. }
  116. } else {
  117. infile = stdin;
  118. }
  119. /* Get the password. */
  120. if (passphrase_entry_readpass(&passwd, passphrase_entry,
  121. passphrase_arg, "Please enter passphrase",
  122. "Please confirm passphrase", dec)) {
  123. warnp("passphrase_entry_readpass");
  124. goto err1;
  125. }
  126. /*-
  127. * If we're decrypting, open the input file and process its header;
  128. * doing this here allows us to abort without creating an output
  129. * file if the input file does not have a valid scrypt header or if
  130. * we have the wrong passphrase.
  131. *
  132. * If successful, we get back a cookie containing the decryption
  133. * parameters (which we'll use after we open the output file).
  134. */
  135. if (dec) {
  136. if ((rc = scryptdec_file_prep(infile, (uint8_t *)passwd,
  137. strlen(passwd), &params, verbose, force_resources,
  138. &C)) != 0) {
  139. goto cleanup;
  140. }
  141. }
  142. /* If we have an output filename, open it. */
  143. if (outfilename != NULL) {
  144. if ((outfile = fopen(outfilename, "wb")) == NULL) {
  145. warnp("Cannot open output file: %s", outfilename);
  146. goto err2;
  147. }
  148. }
  149. /* Encrypt or decrypt. */
  150. if (dec)
  151. rc = scryptdec_file_copy(C, outfile);
  152. else
  153. rc = scryptenc_file(infile, outfile, (uint8_t *)passwd,
  154. strlen(passwd), &params, verbose, force_resources);
  155. cleanup:
  156. /* Free the decryption cookie, if any. */
  157. scryptdec_file_cookie_free(C);
  158. /* Zero and free the password. */
  159. insecure_memzero(passwd, strlen(passwd));
  160. free(passwd);
  161. /* Close any files we opened. */
  162. if ((infile != stdin) && fclose(infile))
  163. warnp("fclose");
  164. if ((outfile != stdout) && fclose(outfile))
  165. warnp("fclose");
  166. /* If we failed, print the right error message and exit. */
  167. if (rc != SCRYPT_OK) {
  168. scryptenc_print_error(rc, infilename, outfilename);
  169. goto err0;
  170. }
  171. /* Success! */
  172. return (0);
  173. err2:
  174. scryptdec_file_cookie_free(C);
  175. insecure_memzero(passwd, strlen(passwd));
  176. free(passwd);
  177. err1:
  178. if ((infile != stdin) && fclose(infile))
  179. warnp("fclose");
  180. err0:
  181. /* Failure! */
  182. return (-1);
  183. }
  184. /* Parse a numeric optarg within a GETOPT context. (Requires ch and optarg.) */
  185. #define GETOPT_PARSENUM_WITHIN_UNSIGNED(var, min, max) do { \
  186. if (PARSENUM((var), optarg, (min), (max))) { \
  187. if (errno == ERANGE) { \
  188. warn0("%s must be between %ju and %ju" \
  189. " (inclusive)", ch, (uintmax_t)(min), \
  190. (uintmax_t)(max)); \
  191. } else \
  192. warnp("Invalid option: %s %s", ch, optarg); \
  193. exit(1); \
  194. } \
  195. } while (0)
  196. int
  197. main(int argc, char * argv[])
  198. {
  199. int dec = 0;
  200. int info = 0;
  201. int force_resources = 0;
  202. uint64_t maxmem64;
  203. struct scryptenc_params params = {0, 0.5, 300.0, 0, 0, 0};
  204. const char * ch;
  205. const char * infilename;
  206. const char * outfilename;
  207. int verbose = 0;
  208. enum passphrase_entry passphrase_entry = PASSPHRASE_UNSET;
  209. const char * passphrase_arg;
  210. WARNP_INIT;
  211. /* We should have "enc", "dec", or "info" first. */
  212. if (argc < 2)
  213. usage();
  214. if (strcmp(argv[1], "enc") == 0) {
  215. params.maxmem = 0;
  216. params.maxmemfrac = 0.125;
  217. params.maxtime = 5.0;
  218. } else if (strcmp(argv[1], "dec") == 0) {
  219. dec = 1;
  220. } else if (strcmp(argv[1], "info") == 0) {
  221. info = 1;
  222. } else if (strcmp(argv[1], "--version") == 0) {
  223. fprintf(stdout, "scrypt %s\n", PACKAGE_VERSION);
  224. exit(0);
  225. } else {
  226. warn0("First argument must be 'enc', 'dec', or 'info'.");
  227. usage();
  228. }
  229. argc--;
  230. argv++;
  231. /* Parse arguments. */
  232. while ((ch = GETOPT(argc, argv)) != NULL) {
  233. GETOPT_SWITCH(ch) {
  234. GETOPT_OPT("-f"):
  235. force_resources = 1;
  236. break;
  237. GETOPT_OPTARG("--logN"):
  238. GETOPT_PARSENUM_WITHIN_UNSIGNED(&params.logN, 10, 40);
  239. break;
  240. GETOPT_OPTARG("-M"):
  241. if (humansize_parse(optarg, &maxmem64)) {
  242. warn0("Could not parse the parameter to -M.");
  243. exit(1);
  244. }
  245. if (maxmem64 > SIZE_MAX) {
  246. warn0("The parameter to -M is too large.");
  247. exit(1);
  248. }
  249. params.maxmem = (size_t)maxmem64;
  250. break;
  251. GETOPT_OPTARG("-m"):
  252. if (PARSENUM(&params.maxmemfrac, optarg, 0, 0.5)) {
  253. warnp("Invalid option: -m %s", optarg);
  254. exit(1);
  255. }
  256. break;
  257. GETOPT_OPTARG("-p"):
  258. GETOPT_PARSENUM_WITHIN_UNSIGNED(&params.p, 1, 32);
  259. break;
  260. GETOPT_OPTARG("--passphrase"):
  261. if (passphrase_entry != PASSPHRASE_UNSET) {
  262. warn0("You can only enter one --passphrase or"
  263. " -P argument");
  264. exit(1);
  265. }
  266. /* Parse "method:arg" optarg. */
  267. if (passphrase_entry_parse(optarg, &passphrase_entry,
  268. &passphrase_arg))
  269. exit(1);
  270. break;
  271. GETOPT_OPTARG("-r"):
  272. GETOPT_PARSENUM_WITHIN_UNSIGNED(&params.r, 1, 32);
  273. break;
  274. GETOPT_OPTARG("-t"):
  275. if (PARSENUM(&params.maxtime, optarg, 0, INFINITY)) {
  276. warnp("Invalid option: -t %s", optarg);
  277. exit(1);
  278. }
  279. break;
  280. GETOPT_OPT("-v"):
  281. verbose = 1;
  282. break;
  283. GETOPT_OPT("-P"):
  284. if (passphrase_entry != PASSPHRASE_UNSET) {
  285. warn0("You can only enter one --passphrase or"
  286. " -P argument");
  287. exit(1);
  288. }
  289. passphrase_entry = PASSPHRASE_STDIN_ONCE;
  290. passphrase_arg = "";
  291. break;
  292. GETOPT_MISSING_ARG:
  293. warn0("Missing argument to %s", ch);
  294. usage();
  295. GETOPT_DEFAULT:
  296. warn0("illegal option -- %s", ch);
  297. usage();
  298. }
  299. }
  300. argc -= optind;
  301. argv += optind;
  302. /* We must have one or two parameters left. */
  303. if ((argc < 1) || (argc > 2))
  304. usage();
  305. /* The explicit parameters must be zero, or all non-zero. */
  306. if ((params.logN != 0) && ((params.r == 0) || (params.p == 0))) {
  307. warn0("If --logN is set, -r and -p must also be set");
  308. goto err0;
  309. }
  310. if ((params.r != 0) && ((params.logN == 0) || (params.p == 0))) {
  311. warn0("If -r is set, --logN and -p must also be set");
  312. goto err0;
  313. }
  314. if ((params.p != 0) && ((params.logN == 0) || (params.r == 0))) {
  315. warn0("If -p is set, --logN and -r must also be set");
  316. goto err0;
  317. }
  318. /* We can't have a maxmemfrac of 0. */
  319. if (params.maxmemfrac == 0.0) {
  320. warn0("-m must be greater than 0");
  321. goto err0;
  322. }
  323. /* Set the input filename. */
  324. if (strcmp(argv[0], "-"))
  325. infilename = argv[0];
  326. else
  327. infilename = NULL;
  328. /* Set the output filename. */
  329. if (argc > 1)
  330. outfilename = argv[1];
  331. else
  332. outfilename = NULL;
  333. /* Set the default passphrase entry method. */
  334. if (passphrase_entry == PASSPHRASE_UNSET) {
  335. passphrase_entry = PASSPHRASE_TTY_STDIN;
  336. passphrase_arg = "";
  337. }
  338. /* Sanity check passphrase entry method and input filename. */
  339. if ((passphrase_entry == PASSPHRASE_STDIN_ONCE) &&
  340. (infilename == NULL)) {
  341. warn0("Cannot read both passphrase and input file"
  342. " from standard input");
  343. goto err0;
  344. }
  345. /* What type of operation are we doing? */
  346. if (info) {
  347. /* User selected 'info' mode. */
  348. if (scrypt_mode_info(infilename))
  349. goto err0;
  350. } else {
  351. /* User selected encryption or decryption. */
  352. if (scrypt_mode_enc_dec(params, passphrase_entry,
  353. passphrase_arg, dec, verbose, force_resources,
  354. infilename, outfilename))
  355. goto err0;
  356. }
  357. /* Success! */
  358. exit(0);
  359. err0:
  360. /* Failure! */
  361. exit(1);
  362. }