openssl-enc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /* LibTomCrypt, modular cryptographic library -- Tom St Denis */
  2. /* SPDX-License-Identifier: Unlicense */
  3. /*
  4. * Demo to do the rough equivalent of:
  5. *
  6. * openssl enc -aes-256-cbc -pass pass:foobar -in infile -out outfile -p
  7. *
  8. * Compilation:
  9. *
  10. * $(CC) -I /path/to/headers -L .../libs \
  11. * -o openssl-enc \
  12. * openssl-enc.c -ltomcrypt
  13. *
  14. * Usage:
  15. *
  16. * ./openssl-enc <enc|dec> infile outfile "passphrase" [salt]
  17. *
  18. * If provided, the salt must be EXACTLY a 16-char hex string.
  19. *
  20. * Demo is an example of:
  21. *
  22. * - (When decrypting) yanking salt out of the OpenSSL "Salted__..." header
  23. * - OpenSSL-compatible key derivation (in OpenSSL's modified PKCS#5v1 approach)
  24. * - Grabbing an Initialization Vector from the key generator
  25. * - Performing simple block encryption using AES
  26. *
  27. * This program is free for all purposes without any express guarantee it
  28. * works. If you really want to see a license here, assume the WTFPL :-)
  29. *
  30. * BJ Black, [email protected], https://wjblack.com
  31. *
  32. * BUGS:
  33. * Passing a password on a command line is a HORRIBLE idea. Don't use
  34. * this program for serious work!
  35. */
  36. #include <tomcrypt.h>
  37. #include <termios.h>
  38. #if !defined(LTC_RIJNDAEL) || !defined(LTC_CBC_MODE) || !defined(LTC_PKCS_5) || !defined(LTC_RNG_GET_BYTES) || !defined(LTC_MD5)
  39. int main(void)
  40. {
  41. return -1;
  42. }
  43. #else
  44. /* OpenSSL by default only runs one hash round */
  45. #define OPENSSL_ITERATIONS 1
  46. /* Use aes-256-cbc, so 256 bits of key, 128 of IV */
  47. #define KEY_LENGTH (256>>3)
  48. #define IV_LENGTH (128>>3)
  49. /* PKCS#5v1 requires exactly an 8-byte salt */
  50. #define SALT_LENGTH 8
  51. /* The header OpenSSL puts on an encrypted file */
  52. static char salt_header[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' };
  53. #include <errno.h>
  54. #include <stdio.h>
  55. #include <string.h>
  56. /* A simple way to handle the possibility that a block may increase in size
  57. after padding. */
  58. union paddable {
  59. unsigned char unpad[1024];
  60. unsigned char pad[1024+MAXBLOCKSIZE];
  61. };
  62. /*
  63. * Print usage and exit with a bad status (and perror() if any errno).
  64. *
  65. * Input: argv[0] and the error string
  66. * Output: <no return>
  67. * Side Effects: print messages and barf (does exit(3))
  68. */
  69. static void LTC_NORETURN barf(const char *pname, const char *err)
  70. {
  71. FILE* o = err == NULL ? stdout : stderr;
  72. fprintf(o,
  73. "Usage: %s <enc|dec> infile outfile [passphrase | -] [salt]\n"
  74. "\n"
  75. " The passphrase can either be given at the command line\n"
  76. " or if it's passed as '-' it will be read interactively.\n"
  77. "\n"
  78. " # encrypts infile->outfile, random salt\n"
  79. " %s enc infile outfile pass\n"
  80. "\n"
  81. " # encrypts infile->outfile, salt from cmdline\n"
  82. " %s enc infile outfile pass 0123456789abcdef\n"
  83. "\n"
  84. " # decrypts infile->outfile, pulls salt from infile\n"
  85. " %s dec infile outfile pass\n"
  86. "\n"
  87. " # decrypts infile->outfile, salt specified\n"
  88. " # (don't try to read the salt from infile)\n"
  89. " %s dec infile outfile pass 0123456789abcdef\n"
  90. "\n"
  91. "Application Error: %s\n", pname, pname, pname, pname, pname, err ? err : "None");
  92. if(errno)
  93. perror(
  94. " System Error");
  95. exit(err == NULL ? 0 : -1);
  96. }
  97. /*
  98. * Parse the Salted__[+8 bytes] from an OpenSSL-compatible file header.
  99. *
  100. * Input: file to read from and a to put the salt in (exactly 8 bytes!)
  101. * Output: CRYPT_OK if parsed OK, CRYPT_ERROR if not
  102. * Side Effects: infile's read pointer += 16
  103. */
  104. int parse_openssl_header(FILE *in, unsigned char *out)
  105. {
  106. unsigned char tmp[SALT_LENGTH];
  107. if(fread(tmp, 1, sizeof(tmp), in) != sizeof(tmp))
  108. return CRYPT_ERROR;
  109. if(memcmp(tmp, salt_header, sizeof(tmp)))
  110. return CRYPT_ERROR;
  111. if(fread(tmp, 1, sizeof(tmp), in) != sizeof(tmp))
  112. return CRYPT_ERROR;
  113. memcpy(out, tmp, sizeof(tmp));
  114. return CRYPT_OK;
  115. }
  116. /*
  117. * Dump a hexed stream of bytes (convenience func).
  118. *
  119. * Input: buf to read from, length
  120. * Output: none
  121. * Side Effects: bytes printed as a hex blob, no lf at the end
  122. */
  123. void dump_bytes(unsigned char *in, unsigned long len)
  124. {
  125. unsigned long idx;
  126. for(idx=0; idx<len; idx++)
  127. printf("%02hhX", *(in+idx));
  128. }
  129. /*
  130. * Pad or unpad a message using PKCS#7 padding.
  131. * Padding will add 1-(blocksize) bytes and unpadding will remove that amount.
  132. * Set is_padding to 1 to pad, 0 to unpad.
  133. *
  134. * Input: paddable buffer, size read, block length of cipher, mode
  135. * Output: number of bytes after padding resp. after unpadding
  136. * Side Effects: none
  137. */
  138. static size_t s_pkcs7_pad(union paddable *buf, size_t nb, int block_length,
  139. int is_padding)
  140. {
  141. unsigned long length;
  142. if(is_padding) {
  143. length = sizeof(buf->pad);
  144. if (padding_pad(buf->pad, nb, &length, block_length) != CRYPT_OK)
  145. return 0;
  146. return length;
  147. } else {
  148. length = nb;
  149. if (padding_depad(buf->pad, &length, 0) != CRYPT_OK)
  150. return 0;
  151. return length;
  152. }
  153. }
  154. /*
  155. * Perform an encrypt/decrypt operation to/from files using AES+CBC+PKCS7 pad.
  156. * Set encrypt to 1 to encrypt, 0 to decrypt.
  157. *
  158. * Input: in/out files, key, iv, and mode
  159. * Output: CRYPT_OK if no error
  160. * Side Effects: bytes slurped from infile, pushed to outfile, fds updated.
  161. */
  162. int do_crypt(FILE *infd, FILE *outfd, unsigned char *key, unsigned char *iv,
  163. int encrypt)
  164. {
  165. union paddable inbuf, outbuf;
  166. int cipher, ret;
  167. symmetric_CBC cbc;
  168. size_t nb;
  169. /* Register your cipher! */
  170. cipher = register_cipher(&aes_desc);
  171. if(cipher == -1)
  172. return CRYPT_INVALID_CIPHER;
  173. /* Start a CBC session with cipher/key/val params */
  174. ret = cbc_start(cipher, iv, key, KEY_LENGTH, 0, &cbc);
  175. if( ret != CRYPT_OK )
  176. return -1;
  177. do {
  178. /* Get bytes from the source */
  179. nb = fread(inbuf.unpad, 1, sizeof(inbuf.unpad), infd);
  180. if(!nb)
  181. return encrypt ? CRYPT_OK : CRYPT_ERROR;
  182. /* Barf if we got a read error */
  183. if(ferror(infd))
  184. return CRYPT_ERROR;
  185. if(encrypt) {
  186. /* We're encrypting, so pad first (if at EOF) and then
  187. crypt */
  188. if(feof(infd))
  189. nb = s_pkcs7_pad(&inbuf, nb,
  190. aes_desc.block_length, 1);
  191. ret = cbc_encrypt(inbuf.pad, outbuf.pad, nb, &cbc);
  192. if(ret != CRYPT_OK)
  193. return ret;
  194. } else {
  195. /* We're decrypting, so decrypt and then unpad if at
  196. EOF */
  197. ret = cbc_decrypt(inbuf.unpad, outbuf.unpad, nb, &cbc);
  198. if( ret != CRYPT_OK )
  199. return ret;
  200. if(feof(infd))
  201. nb = s_pkcs7_pad(&outbuf, nb,
  202. aes_desc.block_length, 0);
  203. if(nb == 0)
  204. /* The file didn't decrypt correctly */
  205. return CRYPT_ERROR;
  206. }
  207. /* Push bytes to outfile */
  208. if(fwrite(outbuf.unpad, 1, nb, outfd) != nb)
  209. return CRYPT_ERROR;
  210. } while(!feof(infd));
  211. /* Close up */
  212. cbc_done(&cbc);
  213. return CRYPT_OK;
  214. }
  215. static char* getpassword(const char *prompt, size_t maxlen)
  216. {
  217. char *wr, *end, *pass = XCALLOC(1, maxlen + 1);
  218. struct termios tio;
  219. tcflag_t c_lflag;
  220. if (pass == NULL)
  221. return NULL;
  222. wr = pass;
  223. end = pass + maxlen;
  224. tcgetattr(0, &tio);
  225. c_lflag = tio.c_lflag;
  226. tio.c_lflag &= ~ECHO;
  227. tcsetattr(0, TCSANOW, &tio);
  228. printf("%s", prompt);
  229. fflush(stdout);
  230. while (pass < end) {
  231. int c = getchar();
  232. if (c == '\r' || c == '\n' || c == -1)
  233. break;
  234. *wr++ = c;
  235. }
  236. tio.c_lflag = c_lflag;
  237. tcsetattr(0, TCSAFLUSH, &tio);
  238. printf("\n");
  239. return pass;
  240. }
  241. /* Convenience macro for the various barfable places below */
  242. #define BARF(a) { \
  243. if(password) free(password); \
  244. if(infd) fclose(infd); \
  245. if(outfd) { fclose(outfd); remove(argv[3]); } \
  246. barf(argv[0], a); \
  247. }
  248. /*
  249. * The main routine. Mostly validate cmdline params, open files, run the KDF,
  250. * and do the crypt.
  251. */
  252. int main(int argc, char *argv[]) {
  253. unsigned char salt[SALT_LENGTH];
  254. FILE *infd = NULL, *outfd = NULL;
  255. int encrypt = -1;
  256. int hash = -1;
  257. int ret;
  258. unsigned char keyiv[KEY_LENGTH + IV_LENGTH];
  259. unsigned long keyivlen = (KEY_LENGTH + IV_LENGTH);
  260. unsigned char *key, *iv;
  261. const void *pass;
  262. char *password = NULL;
  263. unsigned long saltlen = sizeof(salt);
  264. if (argc > 1 && strstr(argv[1], "-h"))
  265. barf(argv[0], NULL);
  266. /* Check proper number of cmdline args */
  267. if(argc < 5 || argc > 6)
  268. BARF("Invalid number of arguments");
  269. /* Check proper mode of operation */
  270. if (!strncmp(argv[1], "enc", 3))
  271. encrypt = 1;
  272. else if(!strncmp(argv[1], "dec", 3))
  273. encrypt = 0;
  274. else
  275. BARF("Bad command name");
  276. /* Check we can open infile/outfile */
  277. infd = fopen(argv[2], "rb");
  278. if(infd == NULL)
  279. BARF("Could not open infile");
  280. outfd = fopen(argv[3], "wb");
  281. if(outfd == NULL)
  282. BARF("Could not open outfile");
  283. /* Get the salt from wherever */
  284. if(argc == 6) {
  285. /* User-provided */
  286. if(base16_decode(argv[5], strlen(argv[5]), salt, &saltlen) != CRYPT_OK)
  287. BARF("Bad user-specified salt");
  288. } else if(encrypt) {
  289. /* Encrypting; get from RNG */
  290. if(rng_get_bytes(salt, sizeof(salt), NULL) != sizeof(salt))
  291. BARF("Not enough random data");
  292. } else {
  293. /* Parse from infile (decrypt only) */
  294. if(parse_openssl_header(infd, salt) != CRYPT_OK)
  295. BARF("Invalid OpenSSL header in infile");
  296. }
  297. /* Fetch the MD5 hasher for PKCS#5 */
  298. hash = register_hash(&md5_desc);
  299. if(hash == -1)
  300. BARF("Could not register MD5 hash");
  301. /* Set things to a sane initial state */
  302. zeromem(keyiv, sizeof(keyiv));
  303. key = keyiv + 0; /* key comes first */
  304. iv = keyiv + KEY_LENGTH; /* iv comes next */
  305. if (argv[4] && strcmp(argv[4], "-")) {
  306. pass = argv[4];
  307. } else {
  308. password = getpassword("Enter password: ", 256);
  309. if (!password)
  310. BARF("Could not get password");
  311. pass = password;
  312. }
  313. /* Run the key derivation from the provided passphrase. This gets us
  314. the key and iv. */
  315. ret = pkcs_5_alg1_openssl(pass, XSTRLEN(pass), salt,
  316. OPENSSL_ITERATIONS, hash, keyiv, &keyivlen );
  317. if(ret != CRYPT_OK)
  318. BARF("Could not derive key/iv from passphrase");
  319. /* Display the salt/key/iv like OpenSSL cmdline does when -p */
  320. printf("salt="); dump_bytes(salt, sizeof(salt)); printf("\n");
  321. printf("key="); dump_bytes(key, KEY_LENGTH); printf("\n");
  322. printf("iv ="); dump_bytes(iv, IV_LENGTH ); printf("\n");
  323. /* If we're encrypting, write the salt header as OpenSSL does */
  324. if(!strncmp(argv[1], "enc", 3)) {
  325. if(fwrite(salt_header, 1, sizeof(salt_header), outfd) !=
  326. sizeof(salt_header) )
  327. BARF("Error writing salt header to outfile");
  328. if(fwrite(salt, 1, sizeof(salt), outfd) != sizeof(salt))
  329. BARF("Error writing salt to outfile");
  330. }
  331. /* At this point, the files are open, the salt has been figured out,
  332. and we're ready to pump data through crypt. */
  333. /* Do the crypt operation */
  334. if(do_crypt(infd, outfd, key, iv, encrypt) != CRYPT_OK)
  335. BARF("Error during crypt operation");
  336. /* Clean up */
  337. if(password) free(password);
  338. fclose(infd); fclose(outfd);
  339. return 0;
  340. }
  341. #endif