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 }