1 /// Implementation of Anura digester. 2 module tern.digest.anura; 3 4 // TODO: Fix? 5 import tern.digest; 6 import tern.digest.circe; 7 import tern.serialization; 8 import tern.algorithm; 9 10 /** 11 * Implementation of Anura256 digester. 12 * 13 * Anura256 is a fast and efficient pseudo-fiestel block based block cipher 14 * that primarily works by shuffling data around and works with blocks of 64 bits. 15 * It utilizes a 256-bit key for encryption and decryption. 16 * 17 * Example: 18 * ```d 19 * import tern.digest.anura; 20 * 21 * ubyte[] data = [1, 2, 3, 4, 5]; 22 * string key = "0123456789ABCDEF0123456789ABCDEF"; // Must be 32 bytes (256 bits) 23 * Anura256.encrypt(data, key); 24 * Anura256.decrypt(data, key); 25 * ``` 26 */ 27 public static @digester class Anura256 28 { 29 public: 30 static: 31 pure: 32 /** 33 * Encrypts the given data using Anura256 algorithm. 34 * 35 * Params: 36 * data = The data to be encrypted. 37 * key = The encryption key, must be 256 bits (32 bytes). 38 */ 39 void encrypt(ref ubyte[] data, string key) 40 { 41 if (key.length != 32) 42 throw new Throwable("Key is not 256 bits!"); 43 44 key = cast(string)digest!Circe(cast(ubyte[])key[0..32]); 45 ulong rola = (cast(ulong*)key.ptr)[1]; 46 ulong rolb = (cast(ulong*)key.ptr)[2]; 47 ulong rolc = (cast(ulong*)key.ptr)[3]; 48 ulong rold = (cast(ulong*)key.ptr)[4]; 49 50 ulong[8] set = [ 51 rola, 52 rolb, 53 rolc, 54 rold, 55 rola << rolb, 56 rolb << rolc, 57 rolc << rold, 58 rold << rola, 59 ]; 60 61 foreach (i; 0..(rola % 128)) 62 { 63 size_t factor = ((set[i % 8] * i) % ((data.length / 16_384) | 2)) | 1; 64 for (size_t j = factor; j < data.length; j += factor) 65 data.swap(j, j - factor); 66 } 67 68 void swap(ref ulong block) 69 { 70 uint left = (cast(uint*)&block)[0]; 71 (cast(uint*)&block)[0] = (cast(uint*)&block)[1]; 72 (cast(uint*)&block)[1] = left; 73 } 74 75 foreach (i; 0..4) 76 { 77 foreach (j, ref block; data.portionTo!(ulong)) 78 { 79 size_t ri = ~j; 80 size_t si = j % 8; 81 block += (rola << si) ^ ri; 82 block ^= (rolb << si) ^ ri; 83 swap(block); 84 block -= (rolc << si) ^ ri; 85 block ^= (rold << si) ^ ri; 86 } 87 88 foreach (j, ref block; (cast(ulong[])data)[1..$]) 89 block ^= data[j]; 90 } 91 } 92 93 /** 94 * Decrypts the given data using Anura256 algorithm. 95 * 96 * Params: 97 * data = The data to be decrypted. 98 * key = The decryption key, must be 256 bits (32 bytes). 99 */ 100 void decrypt(ref ubyte[] data, string key) 101 { 102 if (key.length != 32) 103 throw new Throwable("Key is not 256 bits!"); 104 105 key = cast(string)digest!Circe(cast(ubyte[])key[0..32]); 106 ulong rola = (cast(ulong*)key.ptr)[1]; 107 ulong rolb = (cast(ulong*)key.ptr)[2]; 108 ulong rolc = (cast(ulong*)key.ptr)[3]; 109 ulong rold = (cast(ulong*)key.ptr)[4]; 110 111 ulong[8] set = [ 112 rola, 113 rolb, 114 rolc, 115 rold, 116 rola << rolb, 117 rolb << rolc, 118 rolc << rold, 119 rold << rola, 120 ]; 121 122 if (data.length % 8 != 0) 123 vacpp(data, 8); 124 125 void swap(ref ulong block) 126 { 127 uint left = (cast(uint*)&block)[0]; 128 (cast(uint*)&block)[0] = (cast(uint*)&block)[1]; 129 (cast(uint*)&block)[1] = left; 130 } 131 132 foreach_reverse (i; 0..4) 133 { 134 foreach_reverse (j, ref block; (cast(ulong[])data)[1..$]) 135 block ^= data[j]; 136 137 foreach_reverse (j, ref block; cast(ulong[])data) 138 { 139 size_t ri = ~j; 140 size_t si = j % 8; 141 block ^= (rold << si) ^ ri; 142 block += (rolc << si) ^ ri; 143 swap(block); 144 block ^= (rolb << si) ^ ri; 145 block -= (rola << si) ^ ri; 146 } 147 } 148 149 foreach_reverse (i; 0..(rola % 128)) 150 { 151 size_t factor = ((set[i % 8] * i) % ((data.length / 16_384) | 2)) | 1; 152 for (size_t j = factor; j < data.length; j += factor) 153 data.swap(j, j - factor); 154 } 155 156 unvacpp(data); 157 } 158 } 159 160 /** 161 * Implementation of Anura1024 digester. 162 * 163 * Anura1024 is a variant of Anura cipher that utilizes a 1024-bit key for encryption 164 * and decryption. 165 * 166 * Example: 167 * ```d 168 * import tern.digest.anura; 169 * 170 * ubyte[] data = [1, 2, 3, 4, 5]; 171 * string key = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; // Must be 128 bytes (1024 bits) 172 * Anura1024.encrypt(data, key); 173 * Anura1024.decrypt(data, key); 174 * ``` 175 */ 176 public static @digester class Anura1024 177 { 178 public: 179 static: 180 pure: 181 /** 182 * Encrypts the given data using Anura1024 algorithm. 183 * 184 * Params: 185 * data = The data to be encrypted. 186 * key = The encryption key, must be 1024 bits (128 bytes). 187 */ 188 void encrypt(ref ubyte[] data, string key) 189 { 190 if (key.length != 128) 191 throw new Throwable("Key is not 1024 bits!"); 192 193 key = cast(string)digest!Circe(cast(ubyte[])key[0..32], 0xFC2AB8FFFFF)~ 194 cast(string)digest!Circe(cast(ubyte[])key[32..64], 0xCCABB72DA)~ 195 cast(string)digest!Circe(cast(ubyte[])key[64..96], 0xABC0001700)~ 196 cast(string)digest!Circe(cast(ubyte[])key[96..128], 0x0000D7FFF); 197 ulong rola = (cast(ulong*)key.ptr)[0] ^ (cast(ulong*)key.ptr)[1] | 32; 198 ulong rolb = (cast(ulong*)key.ptr)[2] ^ (cast(ulong*)key.ptr)[3] | 32; 199 ulong rolc = (cast(ulong*)key.ptr)[4] ^ (cast(ulong*)key.ptr)[5] | 32; 200 ulong rold = (cast(ulong*)key.ptr)[6] ^ (cast(ulong*)key.ptr)[7] | 32; 201 ulong roba = (cast(ulong*)key.ptr)[12] ^ (cast(ulong*)key.ptr)[8] | rola; 202 ulong robb = (cast(ulong*)key.ptr)[13] ^ (cast(ulong*)key.ptr)[9] | rolb; 203 ulong robc = (cast(ulong*)key.ptr)[14] ^ (cast(ulong*)key.ptr)[10] | rolc; 204 ulong robd = (cast(ulong*)key.ptr)[15] ^ (cast(ulong*)key.ptr)[11] | rold; 205 206 ulong[16] set = [ 207 rola, 208 rolb, 209 rolc, 210 rold, 211 roba, 212 robb, 213 robc, 214 robd, 215 rola ^ roba, 216 rolb ^ robb, 217 rolc ^ robc, 218 rold ^ robd, 219 rola * roba, 220 rolb * robb, 221 rolc * robc, 222 rold * robd, 223 ]; 224 225 vacpp(data, 8); 226 227 foreach (i; 0..(roba % 128)) 228 { 229 size_t factor = ((set[i % 16] * i) % ((data.length / 16_384) | 2)) | 1; 230 for (size_t j = factor; j < data.length; j += factor) 231 data.swap(j, j - factor); 232 } 233 234 void swap(ref ulong block) 235 { 236 uint left = (cast(uint*)&block)[0]; 237 (cast(uint*)&block)[0] = (cast(uint*)&block)[1]; 238 (cast(uint*)&block)[1] = left; 239 } 240 241 foreach (i; 0..2) 242 { 243 foreach (j, ref block; cast(ulong[])data) 244 { 245 size_t ri = ~i; 246 size_t si = i % 8; 247 block += (rola << si) ^ ri; 248 block ^= (robb << si) ^ ri; 249 swap(block); 250 block -= (rolc << si) ^ ri; 251 block ^= (robd << si) ^ ri; 252 } 253 254 foreach (j, ref block; (cast(ulong[])data)[1..$]) 255 block ^= data.ptr[j - 1]; 256 } 257 } 258 259 /** 260 * Decrypts the given data using Anura1024 algorithm. 261 * 262 * Params: 263 * data = The data to be decrypted. 264 * key = The decryption key, must be 1024 bits (128 bytes). 265 */ 266 void decrypt(ref ubyte[] data, string key) 267 { 268 if (key.length != 128) 269 throw new Throwable("Key is not 1024 bits!"); 270 271 key = cast(string)digest!Circe(cast(ubyte[])key[0..32], 0xFC2AB8FFFFF)~ 272 cast(string)digest!Circe(cast(ubyte[])key[32..64], 0xCCABB72DA)~ 273 cast(string)digest!Circe(cast(ubyte[])key[64..96], 0xABC0001700)~ 274 cast(string)digest!Circe(cast(ubyte[])key[96..128], 0x0000D7FFF); 275 ulong rola = (cast(ulong*)key.ptr)[0] ^ (cast(ulong*)key.ptr)[1] | 32; 276 ulong rolb = (cast(ulong*)key.ptr)[2] ^ (cast(ulong*)key.ptr)[3] | 32; 277 ulong rolc = (cast(ulong*)key.ptr)[4] ^ (cast(ulong*)key.ptr)[5] | 32; 278 ulong rold = (cast(ulong*)key.ptr)[6] ^ (cast(ulong*)key.ptr)[7] | 32; 279 ulong roba = (cast(ulong*)key.ptr)[12] ^ (cast(ulong*)key.ptr)[8] | rola; 280 ulong robb = (cast(ulong*)key.ptr)[13] ^ (cast(ulong*)key.ptr)[9] | rolb; 281 ulong robc = (cast(ulong*)key.ptr)[14] ^ (cast(ulong*)key.ptr)[10] | rolc; 282 ulong robd = (cast(ulong*)key.ptr)[15] ^ (cast(ulong*)key.ptr)[11] | rold; 283 284 ulong[16] set = [ 285 rola, 286 rolb, 287 rolc, 288 rold, 289 roba, 290 robb, 291 robc, 292 robd, 293 rola ^ roba, 294 rolb ^ robb, 295 rolc ^ robc, 296 rold ^ robd, 297 rola * roba, 298 rolb * robb, 299 rolc * robc, 300 rold * robd, 301 ]; 302 303 if (data.length % 8 != 0) 304 vacpp(data, 8); 305 306 void swap(ref ulong block) 307 { 308 uint left = (cast(uint*)&block)[0]; 309 (cast(uint*)&block)[0] = (cast(uint*)&block)[1]; 310 (cast(uint*)&block)[1] = left; 311 } 312 313 foreach_reverse (i; 0..2) 314 { 315 foreach_reverse (j, ref b; (cast(ulong[])data)[1..$]) 316 b ^= data.ptr[j - 1]; 317 318 foreach_reverse (j, ref block; cast(ulong[])data) 319 { 320 size_t ri = ~i; 321 size_t si = i % 8; 322 block ^= (robd << si) ^ ri; 323 block += (rolc << si) ^ ri; 324 swap(block); 325 block ^= (robb << si) ^ ri; 326 block -= (rola << si) ^ ri; 327 } 328 } 329 330 foreach_reverse (i; 0..(roba % 128)) 331 { 332 size_t factor = ((set[i % 16] * i) % ((data.length / 16_384) | 2)) | 1; 333 size_t s = factor; 334 for (; s < data.length; s += factor) { } 335 s -= factor; 336 for (; s >= factor; s -= factor) 337 data.swap(s, s - factor); 338 } 339 340 unvacpp(data); 341 } 342 }