1 /*
2 * (C) 2009 Jack Lloyd
3 *
4 * Distributed under the terms of the Botan license
5 */
6
7 #include <fstream>
8 #include <iostream>
9 #include <sstream>
10 #include <boost/regex.hpp>
11
12 #include <botan/botan.h>
13 #include <botan/eax.h>
14
15 using namespace Botan;
16
17 namespace {
18
from_string(const std::string & s)19 unsigned from_string(const std::string& s)
20 {
21 std::istringstream stream(s);
22 unsigned n;
23 stream >> n;
24 return n;
25 }
26
seq(unsigned n)27 std::string seq(unsigned n)
28 {
29 std::string s;
30
31 for(unsigned i = 0; i != n; ++i)
32 {
33 unsigned char b = (i & 0xFF);
34
35 const char bin2hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
36 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
37
38 s += bin2hex[(b >> 4)];
39 s += bin2hex[(b & 0x0f)];
40 }
41
42 return s;
43 }
44
eax_test(const std::string & algo,const std::string & key_str,const std::string & nonce_str,const std::string & header_str,const std::string & tag_str,const std::string & plaintext_str,const std::string & ciphertext)45 void eax_test(const std::string& algo,
46 const std::string& key_str,
47 const std::string& nonce_str,
48 const std::string& header_str,
49 const std::string& tag_str,
50 const std::string& plaintext_str,
51 const std::string& ciphertext)
52 {
53 /*
54 printf("EAX(algo=%s key=%s nonce=%s header=%s tag=%s pt=%s ct=%s)\n",
55 algo.c_str(), key_str.c_str(), nonce_str.c_str(), header_str.c_str(), tag_str.c_str(),
56 plaintext_str.c_str(), ciphertext.c_str());
57 */
58
59 SymmetricKey key(key_str);
60 InitializationVector iv(nonce_str);
61
62 EAX_Encryption* enc;
63
64 Pipe pipe(new Hex_Decoder,
65 enc = new EAX_Encryption(get_block_cipher(algo)),
66 new Hex_Encoder);
67
68 enc->set_key(key);
69 enc->set_iv(iv);
70
71 OctetString header(header_str);
72
73 enc->set_header(header.begin(), header.length());
74
75 pipe.start_msg();
76 pipe.write(plaintext_str);
77 pipe.end_msg();
78
79 std::string out = pipe.read_all_as_string();
80
81 if(out != ciphertext + tag_str)
82 {
83 printf("BAD enc %s '%s' != '%s%s'\n", algo.c_str(),
84 out.c_str(), ciphertext.c_str(), tag_str.c_str());
85 }
86 else
87 printf("OK enc %s\n", algo.c_str());
88
89 try
90 {
91 EAX_Decryption* dec;
92 Pipe pipe2(new Hex_Decoder,
93 dec = new EAX_Decryption(get_block_cipher(algo)),
94 new Hex_Encoder);
95
96 dec->set_key(key);
97 dec->set_iv(iv);
98
99 dec->set_header(header.begin(), header.length());
100
101 pipe2.start_msg();
102 pipe2.write(ciphertext);
103 pipe2.write(tag_str);
104 pipe2.end_msg();
105
106 std::string out2 = pipe2.read_all_as_string();
107
108 if(out2 != plaintext_str)
109 {
110 printf("BAD decrypt %s '%s'\n", algo.c_str(), out2.c_str());
111 }
112 else
113 printf("OK decrypt %s\n", algo.c_str());
114 }
115 catch(std::exception& e)
116 {
117 printf("%s\n", e.what());
118 }
119
120 }
121
translate_algo(const std::string & in)122 std::pair<std::string, int> translate_algo(const std::string& in)
123 {
124 if(in == "aes (16 byte key)")
125 return std::make_pair("AES-128", 16);
126
127 if(in == "blowfish (8 byte key)")
128 return std::make_pair("Blowfish", 8);
129
130 if(in == "rc2 (8 byte key)")
131 return std::make_pair("RC2", 8);
132
133 if(in == "rc5 (8 byte key)")
134 return std::make_pair("RC5", 8);
135
136 if(in == "rc6 (16 byte key)")
137 return std::make_pair("RC6", 16);
138
139 if(in == "safer-sk128 (16 byte key)")
140 return std::make_pair("SAFER-SK(10)", 16);
141
142 if(in == "twofish (16 byte key)")
143 return std::make_pair("Twofish", 16);
144
145 if(in == "des (8 byte key)")
146 return std::make_pair("DES", 8);
147
148 if(in == "3des (24 byte key)")
149 return std::make_pair("TripleDES", 24);
150
151 // These 3 are disabled due to differences in base algorithm.
152
153 #if 0
154 // XTEA: LTC uses little endian, Botan (and Crypto++) use big-endian
155 // I swapped to LE in XTEA and the vectors did match
156 if(in == "xtea (16 byte key)")
157 return std::make_pair("XTEA", 16);
158
159 // Skipjack: LTC uses big-endian, Botan (and Crypto++) use
160 // little-endian I am not sure if that was the full difference
161 // though, was unable to replicate LTC's EAX vectors with Skipjack
162 if(in == "skipjack (10 byte key)")
163 return std::make_pair("Skipjack", 10);
164
165 // Noekeon: unknown cause, though LTC's lone test vector does not
166 // match Botan
167
168 if(in == "noekeon (16 byte key)")
169 return std::make_pair("Noekeon", 16);
170
171 #endif
172
173 return std::make_pair("", 0);
174 }
175
rep(const std::string & s_in,unsigned n)176 std::string rep(const std::string& s_in, unsigned n)
177 {
178 std::string s_out;
179
180 for(unsigned i = 0; i != n; ++i)
181 s_out += s_in[i % s_in.size()];
182
183 return s_out;
184 }
185
run_tests(std::istream & in)186 void run_tests(std::istream& in)
187 {
188 std::string algo;
189 std::string key;
190
191 while(in.good())
192 {
193 std::string line;
194
195 std::getline(in, line);
196
197 if(line == "")
198 continue;
199
200 if(line.size() > 5 && line.substr(0, 4) == "EAX-")
201 {
202 std::pair<std::string, int> name_and_keylen =
203 translate_algo(line.substr(4));
204
205 algo = name_and_keylen.first;
206 key = seq(name_and_keylen.second);
207 }
208 else if(algo != "")
209 {
210 boost::regex vec_regex("^([ 0-9]{3}): (.*), (.*)$");
211
212 boost::smatch what;
213
214 if(boost::regex_match(line, what, vec_regex, boost::match_extra))
215 {
216 unsigned n = from_string(what[1]);
217 std::string ciphertext = what[2];
218 std::string tag = what[3];
219
220 std::string plaintext = seq(n);
221 std::string header = seq(n);
222 std::string nonce = seq(n);
223
224 eax_test(algo, key, nonce, header, tag,
225 plaintext, ciphertext);
226
227 key = rep(tag, key.size()); // repeat as needed
228 }
229 }
230 }
231
232
233 }
234
235 }
236
main()237 int main()
238 {
239 std::ifstream in("eax.vec");
240
241 Botan::LibraryInitializer init;
242
243 if(!in)
244 {
245 std::cerr << "Couldn't read input file\n";
246 return 1;
247 }
248
249 run_tests(in);
250
251 }
252