1 /// Implementation of Mira digesters. 2 module tern.digest.mira; 3 4 import core.simd; 5 import tern.digest; 6 import tern.digest.circe; 7 import tern.algorithm; 8 9 /** 10 * Implementation of Mira256 digester, internally backed by `tern.digest.circe`. 11 * 12 * Mira is an incredibly fast stream encryption algorithm based on shuffling and vector 13 * xor operations on data. 14 * 15 * Example: 16 * ```d 17 * string key = "SpWc5m7uednxBqV2YrKk83tZ6UayFEPRSpWc5m7uednxBqV2YrKk83tZ6UayFEPR"; 18 * ubyte[] data = cast(ubyte[])"Hello World!"; 19 * Mira256.encrypt(data, key); 20 * Mira256.decrypt(data, key); 21 */ 22 public static @digester class Mira256 23 { 24 public: 25 static: 26 pure: 27 /** 28 * Encrypts the given byte array `data`. 29 * 30 * This method encrypts the data using the Mira algorithm with the specified encryption `key`. 31 * and an optional `seed` value. The encryption is done in place. 32 * 33 * Params: 34 * data = Reference to the byte array to be encrypted. 35 * key = The encryption key as a string. Must be either 256 or 512 bits. 36 * seed = An optional seed value used for encryption. Defaults to 0. 37 */ 38 void encrypt(ref ubyte[] data, string key, ulong seed = 0) 39 { 40 assert(key.length == 32, "Key must be 256 bits!"); 41 42 ubyte[] keyFront = digest!Circe(cast(ubyte[])key, seed); 43 ulong a = (cast(ulong*)keyFront.ptr)[0]; 44 ulong b = (cast(ulong*)keyFront.ptr)[1]; 45 ulong c = (cast(ulong*)keyFront.ptr)[2]; 46 ulong d = (cast(ulong*)keyFront.ptr)[3]; 47 48 size_t rlen = data.length - (data.length % 16); 49 size_t factor = ((a + b + c + d) % ((data.length / 16_384) | 2)) | 1; 50 51 for (size_t i = factor; i < data.length; i += factor) 52 { 53 ubyte b0 = data[i]; 54 data[i] = data[i - factor]; 55 data[i - factor] = b0; 56 } 57 58 foreach (i, ref vec; data.portionTo!ulong2) 59 { 60 size_t ri = ~i; 61 size_t si = i % 8; 62 vec += factor; 63 vec ^= (a << si) ^ i; 64 vec ^= (b << si) + ri; 65 vec ^= (c << si) * ri; 66 vec ^= (d << si) - ri; 67 vec[1] ^= vec[0] << 8; 68 import std.stdio; 69 } 70 71 foreach (i, ref _b; data[rlen..$]) 72 { 73 size_t ri = ~i; 74 size_t si = i % 8; 75 _b += factor; 76 _b ^= (a << si) ^ i; 77 _b ^= (b << si) + ri; 78 _b ^= (c << si) * ri; 79 _b ^= (d << si) - ri; 80 } 81 82 for (size_t i = factor; i < data.length; i += factor) 83 { 84 ubyte b0 = data[i]; 85 data[i] = data[i - factor]; 86 data[i - factor] = b0; 87 } 88 } 89 90 /** 91 * Decrypts the given byte array `data`. 92 * 93 * This method decrypts the data using the Mira algorithm with the specified decryption `key`. 94 * and an optional `seed` value. The decryption is done in place. 95 * 96 * Params: 97 * data = Reference to the byte array to be decrypted. 98 * key = The decryption key as a string. Must be either 256 or 512 bits. 99 * seed = An optional seed value used for decryption. Defaults to 0. 100 */ 101 void decrypt(ref ubyte[] data, string key, ulong seed = 0) 102 { 103 assert(key.length == 32, "Key must be 256 bits!"); 104 105 ubyte[] keyFront = digest!Circe(cast(ubyte[])key, seed); 106 ulong a = (cast(ulong*)keyFront.ptr)[0]; 107 ulong b = (cast(ulong*)keyFront.ptr)[1]; 108 ulong c = (cast(ulong*)keyFront.ptr)[2]; 109 ulong d = (cast(ulong*)keyFront.ptr)[3]; 110 111 size_t factor = ((a + b + c + d) % ((data.length / 16_384) | 2)) | 1; 112 size_t rlen = data.length - (data.length % 16); 113 114 size_t s = factor; 115 for (; s < data.length; s += factor) { } 116 s -= factor; 117 for (; s >= factor; s -= factor) 118 { 119 ubyte b0 = data[s]; 120 data[s] = data[s - factor]; 121 data[s - factor] = b0; 122 } 123 124 ulong2* vptr = cast(ulong2*)data.ptr; 125 foreach (i; 0..(data.length / 16)) 126 { 127 size_t ri = ~i; 128 size_t si = i % 8; 129 *vptr ^= (a << si) ^ ri; 130 *vptr ^= (b << si) ^ ri; 131 *vptr ^= (c << si) ^ ri; 132 *vptr ^= (d << si) ^ ri; 133 *vptr -= factor; 134 vptr += 1; 135 } 136 137 foreach (i, ref _b; data[rlen..$]) 138 { 139 size_t ri = ~i; 140 size_t si = i % 8; 141 _b ^= (d << si) - ri; 142 _b ^= (c << si) * ri; 143 _b ^= (b << si) + ri; 144 _b ^= (a << si) ^ i; 145 _b -= factor; 146 } 147 148 s = factor; 149 for (; s < data.length; s += factor) { } 150 s -= factor; 151 for (; s >= factor; s -= factor) 152 { 153 ubyte b0 = data[s]; 154 data[s] = data[s - factor]; 155 data[s - factor] = b0; 156 } 157 } 158 } 159 160 /** 161 * Implementation of Mira512 digester, internally backed by `tern.digest.circe`. 162 * 163 * Mira is an incredibly fast stream encryption algorithm based on shuffling and vector 164 * xor operations on data. 165 * 166 * Example: 167 * ```d 168 * string key = "SpWc5m7uednxBqV2YrKk83tZ6UayFEPRSpWc5m7uednxBqV2YrKk83tZ6UayFEPR"; 169 * ubyte[] data = cast(ubyte[])"Hello World!"; 170 * Mira512.encrypt(data, key); 171 * Mira512.decrypt(data, key); 172 */ 173 public static @digester class Mira512 174 { 175 public: 176 static: 177 pure: 178 /** 179 * Encrypts the given byte array `data`. 180 * 181 * This method encrypts the data using the Mira algorithm with the specified encryption `key`. 182 * and an optional `seed` value. The encryption is done in place. 183 * 184 * Params: 185 * data = Reference to the byte array to be encrypted. 186 * key = The encryption key as a string. Must be either 256 or 512 bits. 187 * seed = An optional seed value used for encryption. Defaults to 0. 188 */ 189 void encrypt(ref ubyte[] data, string key, ulong seed = 0) 190 { 191 if (key.length != 64) 192 throw new Throwable("Key is not 512 bits!"); 193 194 ubyte[] keyFront = digest!Circe(cast(ubyte[])key[0..32], seed); 195 ubyte[] keyBack = digest!Circe(cast(ubyte[])key[32..64], seed); 196 197 ulong a = (cast(ulong*)keyFront.ptr)[0]; 198 ulong b = (cast(ulong*)keyFront.ptr)[1]; 199 ulong c = (cast(ulong*)keyFront.ptr)[2]; 200 ulong d = (cast(ulong*)keyFront.ptr)[3]; 201 ulong ap = (cast(ulong*)keyBack.ptr)[0]; 202 ulong bp = (cast(ulong*)keyBack.ptr)[1]; 203 ulong cp = (cast(ulong*)keyBack.ptr)[2]; 204 ulong dp = (cast(ulong*)keyBack.ptr)[3]; 205 206 size_t rlen = data.length - (data.length % 16); 207 size_t factor = ((ap + bp + cp + dp) % ((data.length / 16_384) | 2)) | 1; 208 209 for (size_t i = factor; i < data.length; i += factor) 210 { 211 ubyte b0 = data[i]; 212 data[i] = data[i - factor]; 213 data[i - factor] = b0; 214 } 215 216 ulong2* vptr = cast(ulong2*)data.ptr; 217 foreach (i; 0..(data.length / 16)) 218 { 219 size_t ri = ~i; 220 size_t si = i % 8; 221 *vptr += factor; 222 *vptr ^= (a << si) ^ ri; 223 *vptr ^= (b << si) ^ ri; 224 *vptr ^= (c << si) ^ ri; 225 *vptr ^= (d << si) ^ ri; 226 vptr += 1; 227 } 228 229 foreach (i, ref _b; data[rlen..$]) 230 { 231 size_t ri = ~i; 232 size_t si = i % 8; 233 _b += factor; 234 _b ^= (a << si) ^ ri; 235 _b ^= (b << si) ^ ri; 236 _b ^= (c << si) ^ ri; 237 _b ^= (d << si) ^ ri; 238 } 239 } 240 241 /** 242 * Decrypts the given byte array `data`. 243 * 244 * This method decrypts the data using the Mira algorithm with the specified decryption `key`. 245 * and an optional `seed` value. The decryption is done in place. 246 * 247 * Params: 248 * data = Reference to the byte array to be decrypted. 249 * key = The decryption key as a string. Must be either 256 or 512 bits. 250 * seed = An optional seed value used for decryption. Defaults to 0. 251 */ 252 void decrypt(ref ubyte[] data, string key, ulong seed = 0) 253 { 254 if (key.length != 64) 255 throw new Throwable("Key is not 512 bits!"); 256 257 ubyte[] keyFront = digest!Circe(cast(ubyte[])key[0..32], seed); 258 ubyte[] keyBack = digest!Circe(cast(ubyte[])key[32..64], seed); 259 260 ulong a = (cast(ulong*)keyFront.ptr)[0]; 261 ulong b = (cast(ulong*)keyFront.ptr)[1]; 262 ulong c = (cast(ulong*)keyFront.ptr)[2]; 263 ulong d = (cast(ulong*)keyFront.ptr)[3]; 264 ulong ap = (cast(ulong*)keyBack.ptr)[0]; 265 ulong bp = (cast(ulong*)keyBack.ptr)[1]; 266 ulong cp = (cast(ulong*)keyBack.ptr)[2]; 267 ulong dp = (cast(ulong*)keyBack.ptr)[3]; 268 269 size_t rlen = data.length - (data.length % 16); 270 size_t factor = ((ap + bp + cp + dp) % ((data.length / 16_384) | 2)) | 1; 271 272 ulong2* vptr = cast(ulong2*)data.ptr; 273 foreach (i; 0..(data.length / 16)) 274 { 275 size_t ri = ~i; 276 size_t si = i % 8; 277 *vptr ^= (a << si) ^ ri; 278 *vptr ^= (b << si) ^ ri; 279 *vptr ^= (c << si) ^ ri; 280 *vptr ^= (d << si) ^ ri; 281 *vptr -= factor; 282 vptr += 1; 283 } 284 285 foreach (i, ref _b; data[rlen..$]) 286 { 287 size_t ri = ~i; 288 size_t si = i % 8; 289 _b ^= (a << si) ^ ri; 290 _b ^= (b << si) ^ ri; 291 _b ^= (c << si) ^ ri; 292 _b ^= (d << si) ^ ri; 293 _b -= factor; 294 } 295 296 size_t s = factor; 297 for (; s < data.length; s += factor) { } 298 s -= factor; 299 for (; s >= factor; s -= factor) 300 { 301 ubyte b0 = data[s]; 302 data[s] = data[s - factor]; 303 data[s - factor] = b0; 304 } 305 } 306 }