openssl-enc.c 11 KB

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