1 /**
2  * Copyright: Copyright (C) Thomas Dixon 2009. All rights reserved.
3  * License:   BSD style: $(LICENSE)
4  * Authors:   Thomas Dixon
5  */
6 
7 module tango.util.cipher.ChaCha;
8 
9 private import tango.util.cipher.Cipher;
10 private import tango.util.cipher.Salsa20;
11 
12 /** Implementation of ChaCha designed by Daniel J. Bernstein. */
13 class ChaCha : Salsa20
14 {
15     @property override const(char)[] name()
16     {
17         return "ChaCha";
18     }
19     
20     this()
21     {
22         i0 = 12;
23         i1 = 13;
24     }
25 
26     this(bool encrypt, ubyte[] key, ubyte[] iv) {
27         this();
28         init(encrypt, key, iv);
29     }
30 
31     override protected void keySetup()
32     {
33         uint offset;
34         const(ubyte)[] constants;
35         
36         state[4] = ByteConverter.LittleEndian.to!(uint)(workingKey[0..4]);
37         state[5] = ByteConverter.LittleEndian.to!(uint)(workingKey[4..8]);
38         state[6] = ByteConverter.LittleEndian.to!(uint)(workingKey[8..12]);
39         state[7] = ByteConverter.LittleEndian.to!(uint)(workingKey[12..16]);
40         
41         if (workingKey.length == 32)
42         {
43             constants = sigma;
44             offset = 16;
45         } else
46             constants = tau;
47             
48         state[ 8] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset..offset+4]);
49         state[ 9] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+4..offset+8]);
50         state[10] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+8..offset+12]);
51         state[11] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+12..offset+16]);
52         state[ 0] = ByteConverter.LittleEndian.to!(uint)(constants[0..4]);
53         state[ 1] = ByteConverter.LittleEndian.to!(uint)(constants[4..8]);
54         state[ 2] = ByteConverter.LittleEndian.to!(uint)(constants[8..12]);
55         state[ 3] = ByteConverter.LittleEndian.to!(uint)(constants[12..16]);
56     }
57     
58     override protected void ivSetup()
59     {
60         state[12] = state[13] = 0;
61         state[14] = ByteConverter.LittleEndian.to!(uint)(workingIV[0..4]);
62         state[15] = ByteConverter.LittleEndian.to!(uint)(workingIV[4..8]);
63     }
64     
65     override protected void salsa20WordToByte(const(uint[]) input, ref ubyte[] output)
66     {
67         uint[] x = new uint[16];
68         x[] = input[0..16];
69           
70         int i;
71         for (i = 0; i < 4; i++)
72         {
73             x[ 0] += x[ 4]; x[12] = Bitwise.rotateLeft(x[12]^x[ 0], 16u);
74             x[ 8] += x[12]; x[ 4] = Bitwise.rotateLeft(x[ 4]^x[ 8], 12u);
75             x[ 0] += x[ 4]; x[12] = Bitwise.rotateLeft(x[12]^x[ 0],  8u);
76             x[ 8] += x[12]; x[ 4] = Bitwise.rotateLeft(x[ 4]^x[ 8],  7u);
77             x[ 1] += x[ 5]; x[13] = Bitwise.rotateLeft(x[13]^x[ 1], 16u);
78             x[ 9] += x[13]; x[ 5] = Bitwise.rotateLeft(x[ 5]^x[ 9], 12u);
79             x[ 1] += x[ 5]; x[13] = Bitwise.rotateLeft(x[13]^x[ 1],  8u);
80             x[ 9] += x[13]; x[ 5] = Bitwise.rotateLeft(x[ 5]^x[ 9],  7u);
81             x[ 2] += x[ 6]; x[14] = Bitwise.rotateLeft(x[14]^x[ 2], 16u);
82             x[10] += x[14]; x[ 6] = Bitwise.rotateLeft(x[ 6]^x[10], 12u);
83             x[ 2] += x[ 6]; x[14] = Bitwise.rotateLeft(x[14]^x[ 2],  8u);
84             x[10] += x[14]; x[ 6] = Bitwise.rotateLeft(x[ 6]^x[10],  7u);
85             x[ 3] += x[ 7]; x[15] = Bitwise.rotateLeft(x[15]^x[ 3], 16u);
86             x[11] += x[15]; x[ 7] = Bitwise.rotateLeft(x[ 7]^x[11], 12u);
87             x[ 3] += x[ 7]; x[15] = Bitwise.rotateLeft(x[15]^x[ 3],  8u);
88             x[11] += x[15]; x[ 7] = Bitwise.rotateLeft(x[ 7]^x[11],  7u);
89             x[ 0] += x[ 5]; x[15] = Bitwise.rotateLeft(x[15]^x[ 0], 16u);
90             x[10] += x[15]; x[ 5] = Bitwise.rotateLeft(x[ 5]^x[10], 12u);
91             x[ 0] += x[ 5]; x[15] = Bitwise.rotateLeft(x[15]^x[ 0],  8u);
92             x[10] += x[15]; x[ 5] = Bitwise.rotateLeft(x[ 5]^x[10],  7u);
93             x[ 1] += x[ 6]; x[12] = Bitwise.rotateLeft(x[12]^x[ 1], 16u);
94             x[11] += x[12]; x[ 6] = Bitwise.rotateLeft(x[ 6]^x[11], 12u);
95             x[ 1] += x[ 6]; x[12] = Bitwise.rotateLeft(x[12]^x[ 1],  8u);
96             x[11] += x[12]; x[ 6] = Bitwise.rotateLeft(x[ 6]^x[11],  7u);
97             x[ 2] += x[ 7]; x[13] = Bitwise.rotateLeft(x[13]^x[ 2], 16u);
98             x[ 8] += x[13]; x[ 7] = Bitwise.rotateLeft(x[ 7]^x[ 8], 12u);
99             x[ 2] += x[ 7]; x[13] = Bitwise.rotateLeft(x[13]^x[ 2],  8u);
100             x[ 8] += x[13]; x[ 7] = Bitwise.rotateLeft(x[ 7]^x[ 8],  7u);
101             x[ 3] += x[ 4]; x[14] = Bitwise.rotateLeft(x[14]^x[ 3], 16u);
102             x[ 9] += x[14]; x[ 4] = Bitwise.rotateLeft(x[ 4]^x[ 9], 12u);
103             x[ 3] += x[ 4]; x[14] = Bitwise.rotateLeft(x[14]^x[ 3],  8u);
104             x[ 9] += x[14]; x[ 4] = Bitwise.rotateLeft(x[ 4]^x[ 9],  7u);
105         }
106         
107         for (i = 0; i < 16; i++)
108             x[i] += input[i];
109             
110         int j;    
111         for (i = j = 0; i < x.length; i++,j+=int.sizeof)
112             ByteConverter.LittleEndian.from!(uint)(x[i], output[j..j+int.sizeof]);
113     }
114     
115     /** ChaCha test vectors */
116     debug (UnitTest)
117     {
118         unittest
119         {
120             __gshared immutable immutable(char)[][] test_keys = [
121                 "80000000000000000000000000000000", 
122                 "0053a6f94c9ff24598eb3e91e4378add",
123                 "00002000000000000000000000000000"~
124                 "00000000000000000000000000000000",
125                 "0f62b5085bae0154a7fa4da0f34699ec"~
126                 "3f92e5388bde3184d72a7dd02376c91c"
127                 
128             ];
129             
130             __gshared immutable immutable(char)[][] test_ivs = [
131                 "0000000000000000",            
132                 "0d74db42a91077de",
133                 "0000000000000000",
134                 "288ff65dc42b92f9"
135             ];
136                  
137             __gshared immutable immutable(char)[][] test_plaintexts = [
138                 "00000000000000000000000000000000"~
139                 "00000000000000000000000000000000"~
140                 "00000000000000000000000000000000"~
141                 "00000000000000000000000000000000",
142                 
143                 "00000000000000000000000000000000"~
144                 "00000000000000000000000000000000"~
145                 "00000000000000000000000000000000"~
146                 "00000000000000000000000000000000",
147                 
148                 "00000000000000000000000000000000"~
149                 "00000000000000000000000000000000"~
150                 "00000000000000000000000000000000"~
151                 "00000000000000000000000000000000",
152                 
153                 "00000000000000000000000000000000"~
154                 "00000000000000000000000000000000"~
155                 "00000000000000000000000000000000"~
156                 "00000000000000000000000000000000"
157                 
158                 
159             ];
160                  
161             __gshared immutable immutable(char)[][] test_ciphertexts = [
162                 "beb1e81e0f747e43ee51922b3e87fb38"~
163                 "d0163907b4ed49336032ab78b67c2457"~
164                 "9fe28f751bd3703e51d876c017faa435"~
165                 "89e63593e03355a7d57b2366f30047c5",
166                          
167                 "509b267e7266355fa2dc0a25c023fce4"~
168                 "7922d03dd9275423d7cb7118b2aedf22"~
169                 "0568854bf47920d6fc0fd10526cfe7f9"~
170                 "de472835afc73c916b849e91eee1f529",
171                  
172                 "653f4a18e3d27daf51f841a00b6c1a2b"~
173                 "d2489852d4ae0711e1a4a32ad166fa6f"~
174                 "881a2843238c7e17786ba5162bc019d5"~
175                 "73849c167668510ada2f62b4ff31ad04",
176                 
177                 "db165814f66733b7a8e34d1ffc123427"~
178                 "1256d3bf8d8da2166922e598acac70f4"~
179                 "12b3fe35a94190ad0ae2e8ec62134819"~
180                 "ab61addcccfe99d867ca3d73183fa3fd"
181             ];
182 
183             ChaCha cc = new ChaCha();
184             ubyte[] buffer = new ubyte[64];
185             char[] result;
186             for (int i = 0; i < test_keys.length; i++)
187             {
188                 auto key = ByteConverter.hexDecode(test_keys[i]);
189                 auto iv = ByteConverter.hexDecode(test_ivs[i]);
190                 
191                 // Encryption
192                 cc.init(true, key, iv);
193                 cc.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer);
194                 result = ByteConverter.hexEncode(buffer);
195                 assert(result == test_ciphertexts[i],
196                         cc.name()~": ("~result~") != ("~test_ciphertexts[i]~")");           
197                 
198                 // Decryption
199                 cc.init(false, key, iv);
200                 cc.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer);
201                 result = ByteConverter.hexEncode(buffer);
202                 assert(result == test_plaintexts[i],
203                         cc.name()~": ("~result~") != ("~test_plaintexts[i]~")");
204             }   
205         }
206     }
207 }