1 module dcrypt.macs.poly1305;
2 
3 import dcrypt.macs.mac;
4 import dcrypt.blockcipher.blockcipher;
5 import dcrypt.bitmanip;
6 import dcrypt.util: wipe;
7 
8 static assert(isMAC!(Poly1305!void), "Poly1305!void is not a valid mac.");
9 
10 private {
11 	enum ubyte rMaskLow2 = 0xFC;
12 	enum ubyte rMaskHigh4 = 0x0F;
13 }
14 
15 alias Poly1305!void Poly1305Raw;
16 
17 /// Poly1305 MAC.
18 /// 
19 /// Params:
20 /// Cipher	=	A block cipher used to derive the key. Set this to `void` if no block cipher should be used.
21 /// 
22 /// Standard: RFC 7539
23 @safe nothrow @nogc
24 public struct Poly1305(Cipher) if ((isBlockCipher!Cipher && Cipher.blockSize == 16) || is(Cipher == void)) {
25 
26 	static if(useCipher) {
27 		public enum name = "Poly1305-" ~ Cipher.name;
28 	} else {
29 		public enum name = "Poly1305";
30 	}
31 
32 	public enum macSize = blockSize;
33 
34 	private {
35 		enum useCipher = !is(Cipher == void);
36 		enum blockSize = 16;
37 
38 		static if(useCipher) {
39 			Cipher cipher;
40 		}
41 
42 		// Polynomial key
43 		int r0, r1, r2, r3, r4;
44 
45 		// Precomputed 5 * r[1..4]
46 		int s1, s2, s3, s4;
47 
48 		// Encrypted nonce
49 		int k0, k1, k2, k3;
50 
51 		// Accumulating state
52 
53 		// Current block of buffered input
54 		ubyte[blockSize] currentBlock;
55 
56 		// Current offset in input buffer
57 		uint currentBlockOffset = 0;
58 
59 		// Polynomial accumulator
60 		int h0, h1, h2, h3, h4;
61 	}
62 
63 	/// Wipe sensitive data.
64 	~this() {
65 		wipe(r0, r1, r2, r3, r4);
66 		wipe(s1, s2, s3, s4);
67 		wipe(k0, k1, k2, k3);
68 		wipe(h0, h1, h2, h3, h4);
69 
70 		wipe(currentBlock);
71 	}
72 
73 	/// Initializes the Poly1305 MAC.
74 	/// Params:
75 	/// key = 32 byte key.
76 	/// nonce = 16 byte nonce. Required if used with block cipher.
77 	public void start(in ubyte[] key, in ubyte[] nonce = null)
78 	{
79 		setKey(key, nonce);
80 		
81 		reset();
82 	}
83 
84 	private void setKey(in ubyte[] key, in ubyte[] nonce)
85 	in {
86 		static if(useCipher) {
87 			assert(nonce !is null && nonce.length == blockSize, "Poly1305 requires an 256 bit IV when used with a block cipher.");
88 		}
89 
90 		assert(key !is null && key.length == 32, "Poly1305 requires a 32 byte key.");
91 
92 	}
93 	body {
94 
95 		ubyte[16] r, s;
96 		r[] = key[0..16];
97 		clamp(r);
98 		s[] = key[16..32];
99 		
100 		assert(checkKey(r), "Invalid format for r portion of Poly1305 key.");
101 
102 		// Extract r portion of key
103 
104 		int t0 = fromLittleEndian!int(r[0..4]);
105 		int t1 = fromLittleEndian!int(r[4..8]);
106 		int t2 = fromLittleEndian!int(r[8..12]);
107 		int t3 = fromLittleEndian!int(r[12..16]);
108 		
109 		r0 = t0 & 0x3ffffff; t0 >>>= 26; t0 |= t1 << 6;
110 		r1 = t0 & 0x3ffff03; t1 >>>= 20; t1 |= t2 << 12;
111 		r2 = t1 & 0x3ffc0ff; t2 >>>= 14; t2 |= t3 << 18;
112 		r3 = t2 & 0x3f03fff; t3 >>>= 8;
113 		r4 = t3 & 0x00fffff;
114 		
115 		// Precompute multipliers
116 		s1 = r1 * 5;
117 		s2 = r2 * 5;
118 		s3 = r3 * 5;
119 		s4 = r4 * 5;
120 
121 		static if (useCipher)
122 		{
123 			cipher.start(true, s);
124 			cipher.processBlock(nonce, s);
125 		}
126 		
127 		k0 = fromLittleEndian!int(s[0..4]);
128 		k1 = fromLittleEndian!int(s[4..8]);
129 		k2 = fromLittleEndian!int(s[8..12]);
130 		k3 = fromLittleEndian!int(s[12..16]);
131 	}
132 
133 	public void put(in ubyte[] inp...) {
134 
135 		import std.algorithm: min;
136 
137 		const(ubyte)[] input = inp;
138 
139 		uint copied = 0;
140 		while (input.length > 0)
141 		{
142 			if (currentBlockOffset == blockSize)
143 			{
144 				processBlock();
145 				currentBlockOffset = 0;
146 			}
147 
148 			uint toCopy = min(input.length, blockSize - currentBlockOffset);
149 			//System.arraycopy(in, copied + inOff, currentBlock, currentBlockOffset, toCopy);
150 			currentBlock[currentBlockOffset..currentBlockOffset+toCopy] = input[0..toCopy];
151 			input = input[toCopy..$];
152 			copied += toCopy;
153 			currentBlockOffset += toCopy;
154 		}
155 		
156 	}
157 
158 	private void processBlock()
159 	{
160 		if (currentBlockOffset < blockSize)
161 		{
162 			currentBlock[currentBlockOffset] = 1;
163 			for (uint i = currentBlockOffset + 1; i < blockSize; i++)
164 			{
165 				currentBlock[i] = 0;
166 			}
167 		}
168 
169 		immutable long t0 = 0xffffffffL & fromLittleEndian!int(currentBlock[0..4]);
170 		immutable long t1 = 0xffffffffL & fromLittleEndian!int(currentBlock[4..8]);
171 		immutable long t2 = 0xffffffffL & fromLittleEndian!int(currentBlock[8..12]);
172 		immutable long t3 = 0xffffffffL & fromLittleEndian!int(currentBlock[12..16]);
173 		
174 		h0 += t0 & 0x3ffffff;
175 		h1 += (((t1 << 32) | t0) >>> 26) & 0x3ffffff;
176 		h2 += (((t2 << 32) | t1) >>> 20) & 0x3ffffff;
177 		h3 += (((t3 << 32) | t2) >>> 14) & 0x3ffffff;
178 		h4 += (t3 >>> 8);
179 		
180 		if (currentBlockOffset == blockSize)
181 		{
182 			h4 += (1 << 24);
183 		}
184 		
185 		long tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
186 		long tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
187 		long tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
188 		long tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
189 		long tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
190 		
191 		long b;
192 		h0 = cast(int)tp0 & 0x3ffffff; b = (tp0 >>> 26);
193 		tp1 += b; h1 = cast(int)tp1 & 0x3ffffff; b = ((tp1 >>> 26) & 0xffffffff);
194 		tp2 += b; h2 = cast(int)tp2 & 0x3ffffff; b = ((tp2 >>> 26) & 0xffffffff);
195 		tp3 += b; h3 = cast(int)tp3 & 0x3ffffff; b = (tp3 >>> 26);
196 		tp4 += b; h4 = cast(int)tp4 & 0x3ffffff; b = (tp4 >>> 26);
197 		h0 += b * 5;
198 	}
199 
200 	public ubyte[macSize] finish() {
201 		ubyte[macSize] mac;
202 		finish(mac);
203 		return mac;
204 	}
205 
206 	public ubyte[] finish(ubyte[] output)
207 	in {
208 		assert(output.length >= blockSize, "Output buffer is too short.");
209 	}
210 	body {
211 		if (currentBlockOffset > 0)
212 		{
213 			// Process padded final block
214 			processBlock();
215 		}
216 		
217 		long f0, f1, f2, f3;
218 		
219 		int b = h0 >>> 26;
220 		h0 = h0 & 0x3ffffff;
221 		h1 += b; b = h1 >>> 26; h1 = h1 & 0x3ffffff;
222 		h2 += b; b = h2 >>> 26; h2 = h2 & 0x3ffffff;
223 		h3 += b; b = h3 >>> 26; h3 = h3 & 0x3ffffff;
224 		h4 += b; b = h4 >>> 26; h4 = h4 & 0x3ffffff;
225 		h0 += b * 5;
226 		
227 		int g0, g1, g2, g3, g4;
228 		g0 = h0 + 5; b = g0 >>> 26; g0 &= 0x3ffffff;
229 		g1 = h1 + b; b = g1 >>> 26; g1 &= 0x3ffffff;
230 		g2 = h2 + b; b = g2 >>> 26; g2 &= 0x3ffffff;
231 		g3 = h3 + b; b = g3 >>> 26; g3 &= 0x3ffffff;
232 		g4 = h4 + b - (1 << 26);
233 		
234 		b = (g4 >>> 31) - 1;
235 		int nb = ~b;
236 		h0 = (h0 & nb) | (g0 & b);
237 		h1 = (h1 & nb) | (g1 & b);
238 		h2 = (h2 & nb) | (g2 & b);
239 		h3 = (h3 & nb) | (g3 & b);
240 		h4 = (h4 & nb) | (g4 & b);
241 		
242 		f0 = (((h0       ) | (h1 << 26)) & 0xffffffffL) + (0xffffffffL & k0);
243 		f1 = (((h1 >>> 6 ) | (h2 << 20)) & 0xffffffffL) + (0xffffffffL & k1);
244 		f2 = (((h2 >>> 12) | (h3 << 14)) & 0xffffffffL) + (0xffffffffL & k2);
245 		f3 = (((h3 >>> 18) | (h4 << 8 )) & 0xffffffffL) + (0xffffffffL & k3);
246 
247 		toLittleEndian!int(cast(int)f0, output[0..4]);
248 		f1 += (f0 >>> 32);
249 		toLittleEndian!int(cast(int)f1, output[4..8]);
250 		f2 += (f1 >>> 32);
251 		toLittleEndian!int(cast(int)f2, output[8..12]);
252 		f3 += (f2 >>> 32);
253 		toLittleEndian!int(cast(int)f3, output[12..16]);
254 		
255 		reset();
256 		return output[0..blockSize];
257 	}
258 
259 	/// Resets the internal state such that a new MAC can be computed.
260 	public void reset()
261 	{
262 		currentBlockOffset = 0;
263 		h0 = h1 = h2 = h3 = h4 = 0;
264 	}
265 
266 	/// Returns: i1*i2 as long
267 	private long mul32x32_64(in int i1, in int i2) pure
268 	{
269 		return (cast(long)i1) * i2;
270 	}
271 
272 	/// Check if r has right format.
273 	private bool checkKey(in ubyte[] key) pure {
274 		assert(key.length == 16, "r must be 128 bits.");
275 
276 		bool checkMask(in ubyte b, in ubyte mask) pure
277 		{
278 			return (b & (~mask)) == 0;
279 		}
280 
281 		return 
282 			checkMask(key[3], rMaskHigh4) &&
283 				checkMask(key[7], rMaskHigh4) &&
284 				checkMask(key[11], rMaskHigh4) &&
285 				checkMask(key[15], rMaskHigh4) &&
286 				
287 				checkMask(key[4], rMaskLow2) &&
288 				checkMask(key[8], rMaskLow2) &&
289 				checkMask(key[12], rMaskLow2);
290 	}
291 
292 	/// Clears bits in key:
293 	/// Clears top four bits of bytes 3,7,11,15
294 	/// Clears bottom two bits of bytes 4,8,12
295 	/// 
296 	/// Params:
297 	/// key = Some bits of this key get cleared.
298 	private void clamp(ref ubyte[16] key)
299 	{
300 		// r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})
301 		key[3] &= rMaskHigh4;
302 		key[7] &= rMaskHigh4;
303 		key[11] &= rMaskHigh4;
304 		key[15] &= rMaskHigh4;
305 
306 		// r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252}).
307 		key[4] &= rMaskLow2;
308 		key[8] &= rMaskLow2;
309 		key[12] &= rMaskLow2;
310 	}
311 }
312 
313 
314 
315 // Raw Poly1305
316 // onetimeauth.c from nacl-20110221
317 unittest {
318 
319 	poly1305Test!(Poly1305!void)(
320 		x"eea6a7251c1e72916d11c2cb214d3c25 2539121d8e234e652d651fa4c8cff880",
321 		null,
322 		x"8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a
323                         c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738
324                         b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da
325                         99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74e355a5",
326 		x"f3ffc7703f9400e52a7dfb4b3d3305d9"
327 		);
328 	
329 }
330 
331 unittest {
332 	import dcrypt.blockcipher.aes;
333 
334 	poly1305Test!(Poly1305!AES)(
335 		x"0000000000000000000000000000000000000000000000000000000000000000",
336 		x"00000000000000000000000000000000",
337 		x"",
338 		x"66e94bd4ef8a2c3b884cfa59ca342b2e"
339 		);
340 	
341 }
342 
343 unittest {
344 	import dcrypt.blockcipher.aes;
345 	
346 	poly1305Test!(Poly1305!AES)(
347 		x"f795bd0a50e29e0710d3130a20e98d0c f795bd4a52e29ed713d313fa20e98dbc",
348 		x"917cf69ebd68b2ec9b9fe9a3eadda692",
349 		x"66f7",
350 		x"5ca585c75e8f8f025e710cabc9a1508b"
351 		);
352 	
353 }
354 
355 // Test vectors from RFC7539, A.3. #1
356 unittest {
357 	
358 	poly1305Test!(Poly1305!void)(
359 		x"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
360 		null,
361 		x"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
362 		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
363 		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
364 		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
365 		x"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
366 		);
367 	
368 }
369 
370 // Test vectors from RFC7539, A.3. #2
371 unittest {
372 	
373 	poly1305Test!(Poly1305!void)(
374 		x"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 36 e5 f6 b5 c5 e0 60 70 f0 ef ca 96 22 7a 86 3e",
375 		null,
376 		longTestData0,
377 		x"36 e5 f6 b5 c5 e0 60 70 f0 ef ca 96 22 7a 86 3e"
378 		);
379 	
380 }
381 
382 // Test vectors from RFC7539, A.3. #3
383 unittest {
384 	
385 	poly1305Test!(Poly1305!void)(
386 		x"36 e5 f6 b5 c5 e0 60 70 f0 ef ca 96 22 7a 86 3e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
387 		null,
388 		longTestData0,
389 		x"f3 47 7e 7c d9 54 17 af 89 a6 b8 79 4c 31 0c f0"
390 		);
391 	
392 }
393 
394 // Test vectors from RFC7539, A.3. #4
395 unittest {
396 	
397 	poly1305Test!(Poly1305!void)(
398 		x"1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0 47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0",
399 		null,
400 		x"
401 		  27 54 77 61 73 20 62 72 69 6c 6c 69 67 2c 20 61
402 		  6e 64 20 74 68 65 20 73 6c 69 74 68 79 20 74 6f
403 		  76 65 73 0a 44 69 64 20 67 79 72 65 20 61 6e 64
404 		  20 67 69 6d 62 6c 65 20 69 6e 20 74 68 65 20 77
405 		  61 62 65 3a 0a 41 6c 6c 20 6d 69 6d 73 79 20 77
406 		  65 72 65 20 74 68 65 20 62 6f 72 6f 67 6f 76 65
407 		  73 2c 0a 41 6e 64 20 74 68 65 20 6d 6f 6d 65 20
408 		  72 61 74 68 73 20 6f 75 74 67 72 61 62 65 2e
409 		",
410 		x"45 41 66 9a 7e aa ee 61 e7 08 dc 7c bc c5 eb 62"
411 		);
412 	
413 }
414 
415 // Test vectors from RFC7539, A.3. #5
416 // If one uses 130-bit partial reduction, does the code handle the case where partially reduced final result is not fully reduced?
417 unittest {
418 	
419 	poly1305Test!(Poly1305!void)(
420 		x"02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
421 		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
422 		null,
423 		x"
424 		  FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
425 		",
426 		x"03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
427 		);
428 	
429 }
430 
431 // Test vectors from RFC7539, A.3. #6
432 // What happens if addition of s overflows modulo 2^128?
433 unittest {
434 	
435 	poly1305Test!(Poly1305!void)(
436 		x"02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
437 		FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
438 		null,
439 		x"
440 		  02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
441 		",
442 		x"03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
443 		);
444 	
445 }
446 
447 // Test vectors from RFC7539, A.3. #7
448 // What happens if data limb is all ones and there is carry from lower limb?
449 unittest {
450 	
451 	poly1305Test!(Poly1305!void)(
452 		x"01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
453 		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
454 		null,
455 		x"
456 			FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
457 			F0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
458 			11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
459 		",
460 		x"05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
461 		);
462 	
463 }
464 
465 // Test vectors from RFC7539, A.3. #8
466 // What happens if final result from polynomial part is exactly 2^130-5?
467 unittest {
468 	
469 	poly1305Test!(Poly1305!void)(
470 		x"01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
471 		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
472 		null,
473 		x"
474 			FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
475 			FB FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE
476 			01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
477 		",
478 		x"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
479 		);
480 	
481 }
482 
483 // Test vectors from RFC7539, A.3. #9
484 // What happens if final result from polynomial part is exactly 2^130-6?
485 unittest {
486 	
487 	poly1305Test!(Poly1305!void)(
488 		x"02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
489 		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
490 		null,
491 		x"
492 		  FD FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
493 		",
494 		x"FA FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"
495 		);
496 	
497 }
498 
499 // Test vectors from RFC7539, A.3. #10
500 // What happens if 5*H+L-type reduction produces 131-bit intermediate result?
501 unittest {
502 	
503 	poly1305Test!(Poly1305!void)(
504 		x"01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00
505 		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
506 		null,
507 		x"
508 			E3 35 94 D7 50 5E 43 B9 00 00 00 00 00 00 00 00
509 			33 94 D7 50 5E 43 79 CD 01 00 00 00 00 00 00 00
510 			00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
511 			01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
512 		",
513 		x"14 00 00 00 00 00 00 00 55 00 00 00 00 00 00 00"
514 		);
515 	
516 }
517 
518 // Test vectors from RFC7539, A.3. #11
519 // What happens if 5*H+L-type reduction produces131-bit final result?
520 unittest {
521 	
522 	poly1305Test!(Poly1305!void)(
523 		x"01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00
524 		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
525 		null,
526 		x"
527 			E3 35 94 D7 50 5E 43 B9 00 00 00 00 00 00 00 00
528 			33 94 D7 50 5E 43 79 CD 01 00 00 00 00 00 00 00
529 			00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
530 		",
531 		x"13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
532 		);
533 	
534 }
535 
536 version(unittest) {
537 	// Helper function for unittests.
538 private:
539 
540 	void poly1305Test(P)(string key, string iv, string data, string expectedMac) {
541 
542 		alias const(ubyte[]) octets;
543 
544 		P poly;
545 		poly.start(cast(octets) key, cast(octets) iv);
546 		poly.put(cast(octets) data);
547 
548 		assert(poly.finish() == expectedMac, "Poly1305 failed!");
549 		
550 	}
551 
552 	enum longTestData0 = x"
553 		  41 6e 79 20 73 75 62 6d 69 73 73 69 6f 6e 20 74
554 		  6f 20 74 68 65 20 49 45 54 46 20 69 6e 74 65 6e
555 		  64 65 64 20 62 79 20 74 68 65 20 43 6f 6e 74 72
556 		  69 62 75 74 6f 72 20 66 6f 72 20 70 75 62 6c 69
557 		  63 61 74 69 6f 6e 20 61 73 20 61 6c 6c 20 6f 72
558 		  20 70 61 72 74 20 6f 66 20 61 6e 20 49 45 54 46
559 		  20 49 6e 74 65 72 6e 65 74 2d 44 72 61 66 74 20
560 		  6f 72 20 52 46 43 20 61 6e 64 20 61 6e 79 20 73
561 		  74 61 74 65 6d 65 6e 74 20 6d 61 64 65 20 77 69
562 		  74 68 69 6e 20 74 68 65 20 63 6f 6e 74 65 78 74
563 		  20 6f 66 20 61 6e 20 49 45 54 46 20 61 63 74 69
564 		  76 69 74 79 20 69 73 20 63 6f 6e 73 69 64 65 72
565 		  65 64 20 61 6e 20 22 49 45 54 46 20 43 6f 6e 74
566 		  72 69 62 75 74 69 6f 6e 22 2e 20 53 75 63 68 20
567 		  73 74 61 74 65 6d 65 6e 74 73 20 69 6e 63 6c 75
568 		  64 65 20 6f 72 61 6c 20 73 74 61 74 65 6d 65 6e
569 		  74 73 20 69 6e 20 49 45 54 46 20 73 65 73 73 69
570 		  6f 6e 73 2c 20 61 73 20 77 65 6c 6c 20 61 73 20
571 		  77 72 69 74 74 65 6e 20 61 6e 64 20 65 6c 65 63
572 		  74 72 6f 6e 69 63 20 63 6f 6d 6d 75 6e 69 63 61
573 		  74 69 6f 6e 73 20 6d 61 64 65 20 61 74 20 61 6e
574 		  79 20 74 69 6d 65 20 6f 72 20 70 6c 61 63 65 2c
575 		  20 77 68 69 63 68 20 61 72 65 20 61 64 64 72 65
576 		  73 73 65 64 20 74 6f
577 		";
578 }