1 /*
2 * (C) 2009 Jack Lloyd
3 *
4 * Distributed under the terms of the Botan license
5 */
6
7 /*
8 Encrypt a file using a block cipher in CBC mode. Compresses the plaintext
9 with Zlib, MACs with HMAC(SHA-1). Stores the block cipher used in the file,
10 so you don't have to specify it when decrypting.
11
12 What a real application would do (and what this example should do), is test for
13 the presence of the Zlib module, and use it only if it's available. Then add
14 some marker to the stream so the other side knows whether or not the plaintext
15 was compressed. Bonus points for supporting multiple compression schemes.
16
17 Another flaw is that is stores the entire ciphertext in memory, so if the file
18 you're encrypting is 1 Gb... you better have a lot of RAM.
19 */
20
21 #include <fstream>
22 #include <iostream>
23 #include <string>
24 #include <vector>
25 #include <cstring>
26 #include <memory>
27
28 #include <botan/botan.h>
29
30 #if defined(BOTAN_HAS_COMPRESSOR_ZLIB)
31 #include <botan/zlib.h>
32 #endif
33
34 using namespace Botan;
35
36 std::string b64_encode(const SecureVector<byte>&);
37
main(int argc,char * argv[])38 int main(int argc, char* argv[])
39 {
40 if(argc < 2)
41 {
42 std::cout << "Usage: " << argv[0] << " [-c algo] -p passphrase file\n"
43 " -p : Use this passphrase to encrypt\n"
44 " -c : Encrypt with block cipher 'algo' (default 3DES)\n";
45 return 1;
46 }
47
48 Botan::LibraryInitializer init;
49
50 std::string algo = "TripleDES";
51 std::string filename, passphrase;
52
53 // Holy hell, argument processing is a PITA
54 for(int j = 1; argv[j] != 0; j++)
55 {
56 if(std::strcmp(argv[j], "-c") == 0)
57 {
58 if(argv[j+1])
59 {
60 algo = argv[j+1];
61 j++;
62 }
63 else
64 {
65 std::cout << "No argument for -c option" << std::endl;
66 return 1;
67 }
68 }
69 else if(std::strcmp(argv[j], "-p") == 0)
70 {
71 if(argv[j+1])
72 {
73 passphrase = argv[j+1];
74 j++;
75 }
76 else
77 {
78 std::cout << "No argument for -p option" << std::endl;
79 return 1;
80 }
81 }
82 else
83 {
84 if(filename != "")
85 {
86 std::cout << "You can only specify one file at a time\n";
87 return 1;
88 }
89 filename = argv[j];
90 }
91 }
92
93 if(passphrase == "")
94 {
95 std::cout << "You have to specify a passphrase!" << std::endl;
96 return 1;
97 }
98
99 std::ifstream in(filename.c_str(), std::ios::binary);
100 if(!in)
101 {
102 std::cout << "ERROR: couldn't open " << filename << std::endl;
103 return 1;
104 }
105
106 std::string outfile = filename + ".enc";
107 std::ofstream out(outfile.c_str());
108 if(!out)
109 {
110 std::cout << "ERROR: couldn't open " << outfile << std::endl;
111 return 1;
112 }
113
114 try
115 {
116 const BlockCipher* cipher_proto = global_state().algorithm_factory().prototype_block_cipher(algo);
117
118 if(!cipher_proto)
119 {
120 std::cout << "Don't know about the block cipher \"" << algo << "\"\n";
121 return 1;
122 }
123
124 const u32bit key_len = cipher_proto->maximum_keylength();
125 const u32bit iv_len = cipher_proto->block_size();
126
127 AutoSeeded_RNG rng;
128
129 std::auto_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-1)"));
130
131 SecureVector<byte> salt(8);
132 rng.randomize(&salt[0], salt.size());
133
134 const u32bit PBKDF2_ITERATIONS = 8192;
135
136 SymmetricKey bc_key = pbkdf->derive_key(key_len, "BLK" + passphrase,
137 &salt[0], salt.size(),
138 PBKDF2_ITERATIONS);
139
140 InitializationVector iv = pbkdf->derive_key(iv_len, "IVL" + passphrase,
141 &salt[0], salt.size(),
142 PBKDF2_ITERATIONS);
143
144 SymmetricKey mac_key = pbkdf->derive_key(16, "MAC" + passphrase,
145 &salt[0], salt.size(),
146 PBKDF2_ITERATIONS);
147
148 // Just to be all fancy we even write a (simple) header.
149 out << "-------- ENCRYPTED FILE --------" << std::endl;
150 out << algo << std::endl;
151 out << b64_encode(salt) << std::endl;
152
153 Pipe pipe(new Fork(
154 new Chain(new MAC_Filter("HMAC(SHA-1)", mac_key),
155 new Base64_Encoder
156 ),
157 new Chain(
158 #ifdef BOTAN_HAS_COMPRESSOR_ZLIB
159 new Zlib_Compression,
160 #endif
161 get_cipher(algo + "/CBC", bc_key, iv, ENCRYPTION),
162 new Base64_Encoder(true)
163 )
164 )
165 );
166
167 pipe.start_msg();
168 in >> pipe;
169 pipe.end_msg();
170
171 out << pipe.read_all_as_string(0) << std::endl;
172 out << pipe.read_all_as_string(1);
173
174 }
175 catch(Algorithm_Not_Found)
176 {
177 std::cout << "Don't know about the block cipher \"" << algo << "\"\n";
178 return 1;
179 }
180 catch(std::exception& e)
181 {
182 std::cout << "Exception caught: " << e.what() << std::endl;
183 return 1;
184 }
185 return 0;
186 }
187
b64_encode(const SecureVector<byte> & in)188 std::string b64_encode(const SecureVector<byte>& in)
189 {
190 Pipe pipe(new Base64_Encoder);
191 pipe.process_msg(in);
192 return pipe.read_all_as_string();
193 }
194