1 /* This file is part of the Amalthea library. 2 * 3 * Copyright (C) 2018-2021, 2024 Eugene 'Vindex' Stulin 4 * 5 * Distributed under the Boost Software License 1.0 or (at your option) 6 * the GNU Lesser General Public License 3.0 or later. 7 */ 8 9 module amalthea.crypto; 10 11 import std.traits : isSomeChar; 12 13 public import amalthea.libcore; 14 15 import std.digest.md, std.range, std.string; 16 17 18 /******************************************************************************* 19 * The function computes MD5 digest for the file 20 * (with the dmd compiler it works extremely inefficiently). 21 */ 22 string getFileMD5Sum(string filepath) { 23 auto file = File(filepath, "r"); 24 auto md5 = new MD5Digest(); 25 foreach(buffer; file.byChunk(1024)) { 26 md5.put(buffer); 27 } 28 auto hash = md5.finish(); 29 return hash.toHexString.toLower; 30 } 31 32 33 /******************************************************************************* 34 * The function computes Streebog hash sum for the file. 35 */ 36 string getFileStreebogSum(alias bits)(string filepath) 37 if (bits == 256 || bits == 512) { 38 int chunkSize = 64*16; 39 auto streebogObj = new Streebog(bits); 40 auto file = File(filepath, "r"); 41 long filesize = getSize(filepath); 42 while(filesize > 0) { 43 size_t portionSize; 44 if (filesize < chunkSize) { 45 portionSize = cast(size_t)filesize; 46 filesize = 0; 47 } else { 48 portionSize = chunkSize; 49 filesize -= chunkSize; 50 } 51 file.seek(filesize, SEEK_SET); 52 ubyte[] buffer = file.rawRead(new ubyte[portionSize]); 53 streebogObj.pushFront(buffer); 54 } 55 auto hash = streebogObj.finish(); 56 return hash.toHexString.toLower; 57 } 58 59 60 /******************************************************************************* 61 * 256-bit version of file Streebog hash sum. 62 */ 63 string getFileStreebog256Sum(string filepath) { 64 return getFileStreebogSum!256(filepath); 65 } 66 67 68 /******************************************************************************* 69 * 512-bit version of file Streebog hash sum. 70 */ 71 string getFileStreebog512Sum(string filepath) { 72 return getFileStreebogSum!512(filepath); 73 } 74 75 76 unittest { 77 ubyte[68] bytes = 0x25; 78 bytes[0 .. 4] = [0xA1, 0x77, 0x19, 0xFC]; 79 string experimentalFile = "build/test.bin"; 80 std.file.write(experimentalFile, bytes); 81 string expected = Streebog.calcHash!512(bytes).toHexString.toLower; 82 assert(getFileStreebogSum!512(experimentalFile) == expected); 83 expected = Streebog.calcHash!256(bytes).toHexString.toLower; 84 assert(getFileStreebogSum!256(experimentalFile) == expected); 85 std.file.remove(experimentalFile); 86 } 87 88 89 /******************************************************************************* 90 * Auxiliary function of the remainder of the division 91 * for the Streebog algorithm. 92 */ 93 ubyte[64] modulo512(in ubyte[64] a, in ubyte[64] b) { 94 auto t = 0; 95 ubyte[64] c; 96 for(int i = 63; i >= 0; --i) { 97 t = a[i] + b[i] + (t >> 8); 98 c[i] = t & 0xFF; 99 } 100 return c; 101 } 102 103 104 /******************************************************************************* 105 * Auxiliary XOR-function for the Streebog algorithm. 106 */ 107 ubyte[64] xor512(in ubyte[64] a, in ubyte[64] b) { 108 ubyte[64] c; 109 foreach(i; 0 .. 64) { 110 c[i] = a[i] ^ b[i]; 111 } 112 return c; 113 } 114 115 116 /******************************************************************************* 117 * The Streebog class implements GOST R 34.11-2012 118 * for calculating a hash value. 119 */ 120 class Streebog { 121 size_t bitMode; 122 this() { 123 bitMode = 512; 124 } 125 this(size_t bitMode) { 126 enforce(bitMode == 256 || bitMode == 512); 127 this.bitMode = bitMode; 128 } 129 130 /*************************************************************************** 131 * The static template function takes an array of bytes 132 * and returns a hash sum. 133 */ 134 static ubyte[] calcHash(alias bits)(in ubyte[] msg) 135 if (bits == 512 || bits == 256) { 136 ubyte[] message = msg.dup; 137 ubyte[64] v512; 138 v512[$-2] = 0x02; 139 ubyte[64] v0; 140 ubyte[64] sigma; 141 ubyte[64] N; 142 ubyte[64] m; 143 ubyte[64] hash = bits == 512 ? IV512 : IV256; 144 size_t len = message.length*8; 145 while (len >= 512) { 146 m = message[$-64 .. $]; 147 hash = g(N, hash, m); 148 N = modulo512(N, v512); 149 sigma = modulo512(sigma, m); 150 len -= 512; 151 message = message[0 .. $-64]; 152 } 153 m[] = 0; 154 size_t l = len/8 + 1 - ((len & 0x7) == 0); 155 size_t mshift = 63 - len/8 + ((len & 0x7) == 0); 156 m[mshift .. mshift + l] = message[0 .. l]; 157 158 m[63 - len/8] |= (1 << (len & 0x7)); 159 160 hash = g(N, hash, m); 161 v512[63] = len & 0xFF; 162 v512[62] = cast(ubyte)(len >> 8); 163 N = modulo512(N, v512); 164 sigma = modulo512(sigma, m); 165 166 hash = g(v0, hash, N); 167 hash = g(v0, hash, sigma); 168 return bits == 256 ? hash[0 .. $/2].dup : hash.dup; 169 } 170 171 172 /*************************************************************************** 173 * Accepts a sequence of data. 174 * Each new sequence is perceived as preceding relative to the previous one. 175 */ 176 void pushFront(in ubyte[] portion) { 177 ubyte[] message = portion.dup ~ currState.residue; 178 if (currState == ObjectState.init) { 179 currState.hash = bitMode == 512 ? IV512 : IV256; 180 currState.v512[$-2] = 0x02; 181 } 182 183 ulong bitlen = message.length * 8; 184 while (bitlen >= 512) { 185 ubyte[64] m = message[$-64 .. $]; 186 message = message[0 .. $-64]; 187 currState.hash = g(currState.N, currState.hash, m); 188 currState.N = modulo512(currState.N, currState.v512); 189 currState.sigma = modulo512(currState.sigma, m); 190 bitlen -= 512; 191 } 192 currState.residue = message.dup; 193 } 194 195 /*************************************************************************** 196 * Finishes the calculation of the hash sum and returns it. 197 */ 198 ubyte[] finish() { 199 ubyte[64] v0; 200 ubyte[64] m; 201 size_t len = currState.residue.length * 8; 202 size_t l = len/8 + 1 - ((len & 0x7) == 0); 203 size_t mshift = 63 - len/8 + ((len & 0x7) == 0); 204 m[mshift .. mshift + l] = currState.residue[0 .. l]; 205 m[63 - len/8] |= (1 << (len & 0x7)); 206 207 currState.hash = g(currState.N, currState.hash, m); 208 currState.v512[63] = len & 0xFF; 209 currState.v512[62] = cast(ubyte)(len >> 8); 210 currState.N = modulo512(currState.N, currState.v512); 211 currState.sigma = modulo512(currState.sigma, m); 212 213 currState.hash = g(v0, currState.hash, currState.N); 214 currState.hash = g(v0, currState.hash, currState.sigma); 215 return bitMode == 256 ? currState.hash[0 .. $/2].dup 216 : currState.hash.dup; 217 } 218 219 protected: 220 221 struct ObjectState { 222 ubyte[64] hash; 223 ubyte[64] N; 224 ubyte[64] sigma; 225 ubyte[] residue; 226 ubyte[64] v512; 227 } 228 ObjectState currentObjectState; 229 alias currState = currentObjectState; 230 231 232 static immutable ubyte[64] IV512 = 0x00; 233 static immutable ubyte[64] IV256 = 0x01; 234 235 static ubyte[64] S(in ubyte[64] state) { 236 ubyte[64] newState = state; 237 foreach(i; 0 .. 64) { 238 newState[i] = piTable[state[i]]; 239 } 240 return newState; 241 } 242 243 static ubyte[64] P(ubyte[64] state) { 244 ubyte[64] t; 245 foreach(i; 0 .. 64) { 246 t[i] = state[tauTable[i]]; 247 } 248 return t; 249 } 250 251 static ubyte[64] L(ubyte[64] state) { 252 ubyte[64] newState = state; 253 ulong v = 0; 254 foreach(i; 0 .. 8) { 255 v = 0; 256 foreach(k; 0 .. 8) { 257 foreach(j; 0 .. 8) { 258 if ((newState[i*8+k] & (1 << (7-j))) != 0) { 259 v ^= A[k*8+j]; 260 } 261 } 262 } 263 foreach(k; 0 .. 8) { 264 auto value = (v & (0xFFL << (7-k)*8)) >> (7-k)*8; 265 newState[i*8+k] = cast(ubyte)value; 266 } 267 } 268 return newState; 269 } 270 271 static ubyte[64] g(in ubyte[64] N, in ubyte[64] h, in ubyte[64] m) { 272 ubyte[64] K = xor512(h, N); 273 K = L(P(S(K))); 274 ubyte[64] t = E(K, m); 275 t = xor512(h, t); 276 ubyte[64] G = xor512(t, m); 277 return G; 278 } 279 280 static ubyte[64] E(ubyte[64] K, ubyte[64] m) { 281 void keySchedule(size_t i) { 282 K = xor512(K, C[i]); 283 K = L(P(S(K))); 284 } 285 ubyte[64] state = xor512(K, m); 286 foreach(i; 0 .. 12) { 287 state = L(P(S(state))); 288 keySchedule(i); 289 state = xor512(state, K); 290 } 291 return state; 292 } 293 294 static immutable ulong[64] A = [ 295 0x8e20faa72ba0b470, 0x47107ddd9b505a38, 0xad08b0e0c3282d1c, 296 0xd8045870ef14980e, 0x6c022c38f90a4c07, 0x3601161cf205268d, 297 0x1b8e0b0e798c13c8, 0x83478b07b2468764, 0xa011d380818e8f40, 298 0x5086e740ce47c920, 0x2843fd2067adea10, 0x14aff010bdd87508, 299 0x0ad97808d06cb404, 0x05e23c0468365a02, 0x8c711e02341b2d01, 300 0x46b60f011a83988e, 0x90dab52a387ae76f, 0x486dd4151c3dfdb9, 301 0x24b86a840e90f0d2, 0x125c354207487869, 0x092e94218d243cba, 302 0x8a174a9ec8121e5d, 0x4585254f64090fa0, 0xaccc9ca9328a8950, 303 0x9d4df05d5f661451, 0xc0a878a0a1330aa6, 0x60543c50de970553, 304 0x302a1e286fc58ca7, 0x18150f14b9ec46dd, 0x0c84890ad27623e0, 305 0x0642ca05693b9f70, 0x0321658cba93c138, 0x86275df09ce8aaa8, 306 0x439da0784e745554, 0xafc0503c273aa42a, 0xd960281e9d1d5215, 307 0xe230140fc0802984, 0x71180a8960409a42, 0xb60c05ca30204d21, 308 0x5b068c651810a89e, 0x456c34887a3805b9, 0xac361a443d1c8cd2, 309 0x561b0d22900e4669, 0x2b838811480723ba, 0x9bcf4486248d9f5d, 310 0xc3e9224312c8c1a0, 0xeffa11af0964ee50, 0xf97d86d98a327728, 311 0xe4fa2054a80b329c, 0x727d102a548b194e, 0x39b008152acb8227, 312 0x9258048415eb419d, 0x492c024284fbaec0, 0xaa16012142f35760, 313 0x550b8e9e21f7a530, 0xa48b474f9ef5dc18, 0x70a6a56e2440598e, 314 0x3853dc371220a247, 0x1ca76e95091051ad, 0x0edd37c48a08a6d8, 315 0x07e095624504536c, 0x8d70c431ac02a736, 0xc83862965601dd1b, 316 0x641c314b2b8ee083 317 ]; 318 319 static immutable ubyte[256] piTable = [ 320 0xFC, 0xEE, 0xDD, 0x11, 0xCF, 0x6E, 0x31, 0x16, 321 0xFB, 0xC4, 0xFA, 0xDA, 0x23, 0xC5, 0x04, 0x4D, 322 0xE9, 0x77, 0xF0, 0xDB, 0x93, 0x2E, 0x99, 0xBA, 323 0x17, 0x36, 0xF1, 0xBB, 0x14, 0xCD, 0x5F, 0xC1, 324 0xF9, 0x18, 0x65, 0x5A, 0xE2, 0x5C, 0xEF, 0x21, 325 0x81, 0x1C, 0x3C, 0x42, 0x8B, 0x01, 0x8E, 0x4F, 326 0x05, 0x84, 0x02, 0xAE, 0xE3, 0x6A, 0x8F, 0xA0, 327 0x06, 0x0B, 0xED, 0x98, 0x7F, 0xD4, 0xD3, 0x1F, 328 329 0xEB, 0x34, 0x2C, 0x51, 0xEA, 0xC8, 0x48, 0xAB, 330 0xF2, 0x2A, 0x68, 0xA2, 0xFD, 0x3A, 0xCE, 0xCC, 331 0xB5, 0x70, 0x0E, 0x56, 0x08, 0x0C, 0x76, 0x12, 332 0xBF, 0x72, 0x13, 0x47, 0x9C, 0xB7, 0x5D, 0x87, 333 0x15, 0xA1, 0x96, 0x29, 0x10, 0x7B, 0x9A, 0xC7, 334 0xF3, 0x91, 0x78, 0x6F, 0x9D, 0x9E, 0xB2, 0xB1, 335 0x32, 0x75, 0x19, 0x3D, 0xFF, 0x35, 0x8A, 0x7E, 336 0x6D, 0x54, 0xC6, 0x80, 0xC3, 0xBD, 0x0D, 0x57, 337 338 0xDF, 0xF5, 0x24, 0xA9, 0x3E, 0xA8, 0x43, 0xC9, 339 0xD7, 0x79, 0xD6, 0xF6, 0x7C, 0x22, 0xB9, 0x03, 340 0xE0, 0x0F, 0xEC, 0xDE, 0x7A, 0x94, 0xB0, 0xBC, 341 0xDC, 0xE8, 0x28, 0x50, 0x4E, 0x33, 0x0A, 0x4A, 342 0xA7, 0x97, 0x60, 0x73, 0x1E, 0x00, 0x62, 0x44, 343 0x1A, 0xB8, 0x38, 0x82, 0x64, 0x9F, 0x26, 0x41, 344 0xAD, 0x45, 0x46, 0x92, 0x27, 0x5E, 0x55, 0x2F, 345 0x8C, 0xA3, 0xA5, 0x7D, 0x69, 0xD5, 0x95, 0x3B, 346 347 0x07, 0x58, 0xB3, 0x40, 0x86, 0xAC, 0x1D, 0xF7, 348 0x30, 0x37, 0x6B, 0xE4, 0x88, 0xD9, 0xE7, 0x89, 349 0xE1, 0x1B, 0x83, 0x49, 0x4C, 0x3F, 0xF8, 0xFE, 350 0x8D, 0x53, 0xAA, 0x90, 0xCA, 0xD8, 0x85, 0x61, 351 0x20, 0x71, 0x67, 0xA4, 0x2D, 0x2B, 0x09, 0x5B, 352 0xCB, 0x9B, 0x25, 0xD0, 0xBE, 0xE5, 0x6C, 0x52, 353 0x59, 0xA6, 0x74, 0xD2, 0xE6, 0xF4, 0xB4, 0xC0, 354 0xD1, 0x66, 0xAF, 0xC2, 0x39, 0x4B, 0x63, 0xB6 355 ]; 356 357 static immutable ubyte[64] tauTable = [ 358 0, 8, 16, 24, 32, 40, 48, 56, 359 1, 9, 17, 25, 33, 41, 49, 57, 360 2, 10, 18, 26, 34, 42, 50, 58, 361 3, 11, 19, 27, 35, 43, 51, 59, 362 4, 12, 20, 28, 36, 44, 52, 60, 363 5, 13, 21, 29, 37, 45, 53, 61, 364 6, 14, 22, 30, 38, 46, 54, 62, 365 7, 15, 23, 31, 39, 47, 55, 63 366 ]; 367 368 static immutable ubyte[64][12] C = [ 369 [ 370 0xb1, 0x08, 0x5b, 0xda, 0x1e, 0xca, 0xda, 0xe9, 371 0xeb, 0xcb, 0x2f, 0x81, 0xc0, 0x65, 0x7c, 0x1f, 372 0x2f, 0x6a, 0x76, 0x43, 0x2e, 0x45, 0xd0, 0x16, 373 0x71, 0x4e, 0xb8, 0x8d, 0x75, 0x85, 0xc4, 0xfc, 374 0x4b, 0x7c, 0xe0, 0x91, 0x92, 0x67, 0x69, 0x01, 375 0xa2, 0x42, 0x2a, 0x08, 0xa4, 0x60, 0xd3, 0x15, 376 0x05, 0x76, 0x74, 0x36, 0xcc, 0x74, 0x4d, 0x23, 377 0xdd, 0x80, 0x65, 0x59, 0xf2, 0xa6, 0x45, 0x07 378 ],[ 379 0x6f, 0xa3, 0xb5, 0x8a, 0xa9, 0x9d, 0x2f, 0x1a, 380 0x4f, 0xe3, 0x9d, 0x46, 0x0f, 0x70, 0xb5, 0xd7, 381 0xf3, 0xfe, 0xea, 0x72, 0x0a, 0x23, 0x2b, 0x98, 382 0x61, 0xd5, 0x5e, 0x0f, 0x16, 0xb5, 0x01, 0x31, 383 0x9a, 0xb5, 0x17, 0x6b, 0x12, 0xd6, 0x99, 0x58, 384 0x5c, 0xb5, 0x61, 0xc2, 0xdb, 0x0a, 0xa7, 0xca, 385 0x55, 0xdd, 0xa2, 0x1b, 0xd7, 0xcb, 0xcd, 0x56, 386 0xe6, 0x79, 0x04, 0x70, 0x21, 0xb1, 0x9b, 0xb7 387 ],[ 388 0xf5, 0x74, 0xdc, 0xac, 0x2b, 0xce, 0x2f, 0xc7, 389 0x0a, 0x39, 0xfc, 0x28, 0x6a, 0x3d, 0x84, 0x35, 390 0x06, 0xf1, 0x5e, 0x5f, 0x52, 0x9c, 0x1f, 0x8b, 391 0xf2, 0xea, 0x75, 0x14, 0xb1, 0x29, 0x7b, 0x7b, 392 0xd3, 0xe2, 0x0f, 0xe4, 0x90, 0x35, 0x9e, 0xb1, 393 0xc1, 0xc9, 0x3a, 0x37, 0x60, 0x62, 0xdb, 0x09, 394 0xc2, 0xb6, 0xf4, 0x43, 0x86, 0x7a, 0xdb, 0x31, 395 0x99, 0x1e, 0x96, 0xf5, 0x0a, 0xba, 0x0a, 0xb2 396 ],[ 397 0xef, 0x1f, 0xdf, 0xb3, 0xe8, 0x15, 0x66, 0xd2, 398 0xf9, 0x48, 0xe1, 0xa0, 0x5d, 0x71, 0xe4, 0xdd, 399 0x48, 0x8e, 0x85, 0x7e, 0x33, 0x5c, 0x3c, 0x7d, 400 0x9d, 0x72, 0x1c, 0xad, 0x68, 0x5e, 0x35, 0x3f, 401 0xa9, 0xd7, 0x2c, 0x82, 0xed, 0x03, 0xd6, 0x75, 402 0xd8, 0xb7, 0x13, 0x33, 0x93, 0x52, 0x03, 0xbe, 403 0x34, 0x53, 0xea, 0xa1, 0x93, 0xe8, 0x37, 0xf1, 404 0x22, 0x0c, 0xbe, 0xbc, 0x84, 0xe3, 0xd1, 0x2e 405 ],[ 406 0x4b, 0xea, 0x6b, 0xac, 0xad, 0x47, 0x47, 0x99, 407 0x9a, 0x3f, 0x41, 0x0c, 0x6c, 0xa9, 0x23, 0x63, 408 0x7f, 0x15, 0x1c, 0x1f, 0x16, 0x86, 0x10, 0x4a, 409 0x35, 0x9e, 0x35, 0xd7, 0x80, 0x0f, 0xff, 0xbd, 410 0xbf, 0xcd, 0x17, 0x47, 0x25, 0x3a, 0xf5, 0xa3, 411 0xdf, 0xff, 0x00, 0xb7, 0x23, 0x27, 0x1a, 0x16, 412 0x7a, 0x56, 0xa2, 0x7e, 0xa9, 0xea, 0x63, 0xf5, 413 0x60, 0x17, 0x58, 0xfd, 0x7c, 0x6c, 0xfe, 0x57 414 ],[ 415 0xae, 0x4f, 0xae, 0xae, 0x1d, 0x3a, 0xd3, 0xd9, 416 0x6f, 0xa4, 0xc3, 0x3b, 0x7a, 0x30, 0x39, 0xc0, 417 0x2d, 0x66, 0xc4, 0xf9, 0x51, 0x42, 0xa4, 0x6c, 418 0x18, 0x7f, 0x9a, 0xb4, 0x9a, 0xf0, 0x8e, 0xc6, 419 0xcf, 0xfa, 0xa6, 0xb7, 0x1c, 0x9a, 0xb7, 0xb4, 420 0x0a, 0xf2, 0x1f, 0x66, 0xc2, 0xbe, 0xc6, 0xb6, 421 0xbf, 0x71, 0xc5, 0x72, 0x36, 0x90, 0x4f, 0x35, 422 0xfa, 0x68, 0x40, 0x7a, 0x46, 0x64, 0x7d, 0x6e 423 ],[ 424 0xf4, 0xc7, 0x0e, 0x16, 0xee, 0xaa, 0xc5, 0xec, 425 0x51, 0xac, 0x86, 0xfe, 0xbf, 0x24, 0x09, 0x54, 426 0x39, 0x9e, 0xc6, 0xc7, 0xe6, 0xbf, 0x87, 0xc9, 427 0xd3, 0x47, 0x3e, 0x33, 0x19, 0x7a, 0x93, 0xc9, 428 0x09, 0x92, 0xab, 0xc5, 0x2d, 0x82, 0x2c, 0x37, 429 0x06, 0x47, 0x69, 0x83, 0x28, 0x4a, 0x05, 0x04, 430 0x35, 0x17, 0x45, 0x4c, 0xa2, 0x3c, 0x4a, 0xf3, 431 0x88, 0x86, 0x56, 0x4d, 0x3a, 0x14, 0xd4, 0x93 432 ],[ 433 0x9b, 0x1f, 0x5b, 0x42, 0x4d, 0x93, 0xc9, 0xa7, 434 0x03, 0xe7, 0xaa, 0x02, 0x0c, 0x6e, 0x41, 0x41, 435 0x4e, 0xb7, 0xf8, 0x71, 0x9c, 0x36, 0xde, 0x1e, 436 0x89, 0xb4, 0x44, 0x3b, 0x4d, 0xdb, 0xc4, 0x9a, 437 0xf4, 0x89, 0x2b, 0xcb, 0x92, 0x9b, 0x06, 0x90, 438 0x69, 0xd1, 0x8d, 0x2b, 0xd1, 0xa5, 0xc4, 0x2f, 439 0x36, 0xac, 0xc2, 0x35, 0x59, 0x51, 0xa8, 0xd9, 440 0xa4, 0x7f, 0x0d, 0xd4, 0xbf, 0x02, 0xe7, 0x1e 441 ],[ 442 0x37, 0x8f, 0x5a, 0x54, 0x16, 0x31, 0x22, 0x9b, 443 0x94, 0x4c, 0x9a, 0xd8, 0xec, 0x16, 0x5f, 0xde, 444 0x3a, 0x7d, 0x3a, 0x1b, 0x25, 0x89, 0x42, 0x24, 445 0x3c, 0xd9, 0x55, 0xb7, 0xe0, 0x0d, 0x09, 0x84, 446 0x80, 0x0a, 0x44, 0x0b, 0xdb, 0xb2, 0xce, 0xb1, 447 0x7b, 0x2b, 0x8a, 0x9a, 0xa6, 0x07, 0x9c, 0x54, 448 0x0e, 0x38, 0xdc, 0x92, 0xcb, 0x1f, 0x2a, 0x60, 449 0x72, 0x61, 0x44, 0x51, 0x83, 0x23, 0x5a, 0xdb 450 ],[ 451 0xab, 0xbe, 0xde, 0xa6, 0x80, 0x05, 0x6f, 0x52, 452 0x38, 0x2a, 0xe5, 0x48, 0xb2, 0xe4, 0xf3, 0xf3, 453 0x89, 0x41, 0xe7, 0x1c, 0xff, 0x8a, 0x78, 0xdb, 454 0x1f, 0xff, 0xe1, 0x8a, 0x1b, 0x33, 0x61, 0x03, 455 0x9f, 0xe7, 0x67, 0x02, 0xaf, 0x69, 0x33, 0x4b, 456 0x7a, 0x1e, 0x6c, 0x30, 0x3b, 0x76, 0x52, 0xf4, 457 0x36, 0x98, 0xfa, 0xd1, 0x15, 0x3b, 0xb6, 0xc3, 458 0x74, 0xb4, 0xc7, 0xfb, 0x98, 0x45, 0x9c, 0xed 459 ],[ 460 0x7b, 0xcd, 0x9e, 0xd0, 0xef, 0xc8, 0x89, 0xfb, 461 0x30, 0x02, 0xc6, 0xcd, 0x63, 0x5a, 0xfe, 0x94, 462 0xd8, 0xfa, 0x6b, 0xbb, 0xeb, 0xab, 0x07, 0x61, 463 0x20, 0x01, 0x80, 0x21, 0x14, 0x84, 0x66, 0x79, 464 0x8a, 0x1d, 0x71, 0xef, 0xea, 0x48, 0xb9, 0xca, 465 0xef, 0xba, 0xcd, 0x1d, 0x7d, 0x47, 0x6e, 0x98, 466 0xde, 0xa2, 0x59, 0x4a, 0xc0, 0x6f, 0xd8, 0x5d, 467 0x6b, 0xca, 0xa4, 0xcd, 0x81, 0xf3, 0x2d, 0x1b 468 ],[ 469 0x37, 0x8e, 0xe7, 0x67, 0xf1, 0x16, 0x31, 0xba, 470 0xd2, 0x13, 0x80, 0xb0, 0x04, 0x49, 0xb1, 0x7a, 471 0xcd, 0xa4, 0x3c, 0x32, 0xbc, 0xdf, 0x1d, 0x77, 472 0xf8, 0x20, 0x12, 0xd4, 0x30, 0x21, 0x9f, 0x9b, 473 0x5d, 0x80, 0xef, 0x9d, 0x18, 0x91, 0xcc, 0x86, 474 0xe7, 0x1d, 0xa4, 0xaa, 0x88, 0xe1, 0x28, 0x52, 475 0xfa, 0xf4, 0x17, 0xd5, 0xd9, 0xb2, 0x1b, 0x99, 476 0x48, 0xbc, 0x92, 0x4a, 0xf1, 0x1b, 0xd7, 0x20 477 ] 478 ]; 479 } 480 481 482 unittest { 483 ubyte[] message; 484 ubyte[] hash; 485 string expected; 486 487 string line = 488 "ыверогИ ыкълп яырбарх ан ималертс яром с ътюев ,ицунв ижобиртС ,иртев еС"; 489 import std.encoding : Windows1251String, transcode; 490 Windows1251String cp1251string; 491 transcode(line, cp1251string); 492 message = cast(ubyte[])cp1251string; 493 494 hash = Streebog.calcHash!512(message); 495 expected = "28fbc9bada033b1460642bdcddb90c3f" 496 ~ "b3e56c497ccd0f62b8a2ad4935e85f03" 497 ~ "7613966de4ee00531ae60f3b5a47f8da" 498 ~ "e06915d5f2f194996fcabf2622e6881e"; 499 assert(expected == hash.toHexString.toLower); 500 501 hash = Streebog.calcHash!256(message); 502 expected = "508f7e553c06501d749a66fc28c6cac0" 503 ~ "b005746d97537fa85d9e40904efed29d"; 504 assert(expected == hash.toHexString.toLower); 505 506 message = [ 507 0x32, 0x31, 0x30, 0x39, 0x38, 0x37, 0x36, 0x35, 508 0x34, 0x33, 0x32, 0x31, 0x30, 0x39, 0x38, 0x37, 509 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x39, 510 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 511 0x30, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 512 0x32, 0x31, 0x30, 0x39, 0x38, 0x37, 0x36, 0x35, 513 0x34, 0x33, 0x32, 0x31, 0x30, 0x39, 0x38, 0x37, 514 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30 515 ]; 516 517 hash = Streebog.calcHash!256(message); 518 expected = "00557be5e584fd52a449b16b0251d05d" 519 ~ "27f94ab76cbaa6da890b59d8ef1e159d"; 520 assert(expected == hash.toHexString.toLower); 521 522 hash = Streebog.calcHash!512(message); 523 expected = "486f64c1917879417fef082b3381a4e2" 524 ~ "11c324f074654c38823a7b76f830ad00" 525 ~ "fa1fbae42b1285c0352f227524bc9ab1" 526 ~ "6254288dd6863dccd5b9f54a1ad0541b"; 527 assert(expected == hash.toHexString.toLower); 528 529 auto hashObj = new Streebog(512); 530 hashObj.pushFront(message[56 .. $]); 531 hashObj.pushFront(message[48 .. 56]); 532 hashObj.pushFront(message[32 .. 48]); 533 hashObj.pushFront(message[16 .. 32]); 534 hashObj.pushFront(message[0 .. 16]); 535 assert(expected == hashObj.finish.toHexString.toLower); 536 } 537 538 539 //Atbash, Caeser and Vernam functions inspired 540 //by https://habr.com/ru/post/444176/ 541 542 static immutable dchar[] enAlphabetL; 543 static immutable dchar[] enAlphabetU; 544 static immutable dchar[] ruAlphabetL; 545 static immutable dchar[] ruAlphabetU; 546 shared static this() { 547 enAlphabetL = iota(cast(dchar)'a', cast(dchar)('z'+1)).array; 548 enAlphabetU = enAlphabetL.toUpper; 549 ruAlphabetL = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"d.dup; 550 ruAlphabetU = ruAlphabetL.toUpper; 551 } 552 553 554 /******************************************************************************* 555 * Atbash encryption. 556 * Params: 557 * message = Text for encryption. 558 * alphabets = Array of alphabets to use when flipping. 559 * Returns: Encrypted message. 560 */ 561 immutable(Char)[] atbash(Char)(immutable(Char)[] message, 562 immutable(Char)[][] alphabets) 563 if (isSomeChar!Char) { 564 dchar[] result = message.dup.to!dstring.dup; 565 foreach(ref c; result) { 566 foreach(ABC; alphabets) { 567 foreach(i; 0 .. ABC.length) { 568 if (ABC[i] == c) { 569 c = ABC[ABC.length-i-1]; 570 break; 571 } 572 } 573 } 574 } 575 return result.to!(immutable(Char)[]); 576 } 577 578 579 /******************************************************************************* 580 * A special case of Atbash encryption — for English and Russian alphabets. 581 * Params: 582 * message = Text for encryption. 583 * Returns: Encrypted message. 584 */ 585 immutable(Char)[] atbashEnRu(Char)(immutable(Char)[] message) 586 if (isSomeChar!Char) { 587 alias genstring = immutable(Char)[]; 588 auto alphabets = [enAlphabetL.to!genstring, enAlphabetU.to!genstring, 589 ruAlphabetL.to!genstring, ruAlphabetU.to!genstring]; 590 return atbash(message, alphabets); 591 } 592 unittest { 593 assert(atbashEnRu("Hello, World!") == "Svool, Dliow!"); 594 assert(atbashEnRu("I love habr") == "R olev szyi"); 595 } 596 597 598 /******************************************************************************* 599 * Caesar encryption. 600 * Params: 601 * message = Text for encryption. 602 * alphabets = Array of alphabets for internal shifting. 603 * shift = Numerical value of the shift. 604 * Returns: Encrypted message. 605 */ 606 immutable(Char)[] caesarCypher(Char)(immutable(Char)[] message, 607 immutable(Char)[][] alphabets, 608 ssize_t shift) 609 if (isSomeChar!Char) { 610 dstring[] dAlphabets; 611 foreach(ABC; alphabets) { 612 dAlphabets ~= ABC.to!dstring; 613 } 614 dchar[][] cyphers; 615 cyphers.length = dAlphabets.length; 616 foreach(i, ABC; dAlphabets) { 617 if (shift == 0) { 618 cyphers[i] = ABC.dup; 619 } else if (shift > 0) { 620 cyphers[i] = ABC[$-shift .. $].dup ~ ABC[0 .. $-shift].dup; 621 } else { 622 cyphers[i] = ABC[-shift .. $].dup ~ ABC[0 .. -shift].dup; 623 } 624 } 625 dchar[] result = message.to!dstring.dup; 626 foreach(i, c; message) { 627 foreach(x; 0 .. dAlphabets.length) { 628 auto ABC = dAlphabets[x]; 629 auto cypher = cyphers[x]; 630 foreach(n; 0 .. ABC.length) { 631 if (ABC[n] == c) { 632 result[i] = cypher[n]; 633 } 634 } 635 } 636 } 637 return result.to!dstring.to!(immutable(Char)[]); 638 } 639 unittest { 640 assert(caesarCypher("hello world"d, [enAlphabetL], 4) == "dahhk sknhz"); 641 dstring message, expected; 642 message = "Широкая электрификация южных губерний даст "d 643 ~ "мощный толчок подъёму сельского хозяйства."d; 644 expected = "Удлйёыъ шжаёнлдпдёысдъ щвицр юоьалиде яымн "d 645 ~ "зйфице нйжтйё кйяхбзо мажчмёйюй рйгъемнэы."d; 646 assert(expected == caesarCypher(message, [ruAlphabetU, ruAlphabetL], 5)); 647 } 648 649 650 /******************************************************************************* 651 * Vernam encryption. 652 * Params: 653 * message = Byte array for encryption. 654 * key = Encryption key. 655 * Its length must be equal to the length of the message. 656 * Returns: Encrypted message. 657 */ 658 ubyte[] vernamByteCipher(in ubyte[] message, in ubyte[] key) { 659 assert(message.length == key.length); 660 auto len = message.length; 661 ubyte[] result = new ubyte[](len); 662 foreach(i; 0 .. len) { 663 result[i] = message[i] ^ key[i]; 664 } 665 return result; 666 } 667 inout(T)[] vernamCipher(T)(inout(T)[] message, inout(T)[] key) { 668 ubyte[] byteMessage = cast(ubyte[])message.dup; 669 ubyte[] byteKey = cast(ubyte[])key.dup; 670 return cast(inout(T)[])vernamByteCipher(byteMessage, byteKey); 671 } 672 unittest { 673 auto encryptedMessage = vernamCipher("LONDON", "SYSTEM"); 674 assert("LONDON" != encryptedMessage); 675 auto originalMessage = vernamCipher(encryptedMessage, "SYSTEM"); 676 assert("LONDON" == originalMessage); 677 } 678