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