public static byte[] computeHash(byte[] data) { // initial hash value [§5.3.1] uint[] H = new uint[] { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; // PREPROCESSING // convert data into 512-bit/16-integer blocks arrays of ints [§5.2.1] var l = data.Length / 4 + 2; // length (in 32-bit integers) of data + ‘1’ + appended length var N = (int)Math.Ceiling(l / 16.0); // number of 16-integer-blocks required to hold 'l' ints UInt32[][] M = new UInt32[N][]; //let view = Uint8Array.fromArrayBuffer(data); var view = data; for (var i = 0; i < N; i++) { M[i] = new UInt32[16]; for (var j = 0; j < 16; j++) // encode 4 chars per integer, big-endian encoding { //M[i][j] (uint)( var v1 = i * 64 + j * 4; var vv1 = v1 < view.Length ? view[v1] : 0; var v2 = i * 64 + j * 4 + 1; var vv2 = v2 < view.Length ? view[v2] : 0; var v3 = i * 64 + j * 4 + 2; var vv3 = v3 < view.Length ? view[v3] : 0; var v4 = i * 64 + j * 4 + 3; var vv4 = v4 < view.Length ? view[v4] : 0; M[i][j] = (uint)(vv1 << 24 | vv2 << 16 | vv3 << 8 | vv4); //(view[i * 64 + j * 4] << 24) | (view[i * 64 + j * 4 + 1] << 16) | // (view[i * 64 + j * 4 + 2] << 8) | (view[i * 64 + j * 4 + 3]) // ); } // note running off the end of data is ok 'cos bitwise ops on NaN return 0 } // add trailing '1' bit (+ 0's padding) to string [§5.1.1] M[(uint)Math.Floor(data.Length / 4 / 16.0)][(uint)Math.Floor(data.Length / 4.0) % 16] |= (uint)(0x80 << ((3 - data.Length % 4) * 8)); // add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1] // note: most significant word would be (len-1)*8 >>> 32, but since JS converts // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators M[N - 1][14] = (uint)((data.Length * 8) / Math.Pow(2, 32)); M[N - 1][15] = (uint)((data.Length * 8) & 0xffffffff); // HASH COMPUTATION [§6.1.2] var W = new UInt32[64]; UInt32 a, b, c, d, e, f, g, h; for (var i = 0; i < N; i++) { // 1 - prepare message schedule 'W' for (var t = 0; t < 16; t++) { W[t] = M[i][t]; } for (var t = 16; t < 64; t++) { W[t] = (Sha256.σ1(W[t - 2]) + W[t - 7] + Sha256.σ0(W[t - 15]) + W[t - 16]) & 0xffffffff; } // 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; // 3 - main loop (note 'addition modulo 2^32') for (var t = 0; t < 64; t++) { var T1 = h + Sha256.Σ1(e) + Sha256.Ch(e, f, g) + Sha256.K[t] + W[t]; var T2 = Sha256.Σ0(a) + Sha256.Maj(a, b, c); h = g; g = f; f = e; e = (d + T1) & 0xffffffff; d = c; c = b; b = a; a = (T1 + T2) & 0xffffffff; } // 4 - compute the new intermediate hash value (note 'addition modulo 2^32') H[0] = (H[0] + a) & 0xffffffff; H[1] = (H[1] + b) & 0xffffffff; H[2] = (H[2] + c) & 0xffffffff; H[3] = (H[3] + d) & 0xffffffff; H[4] = (H[4] + e) & 0xffffffff; H[5] = (H[5] + f) & 0xffffffff; H[6] = (H[6] + g) & 0xffffffff; H[7] = (H[7] + h) & 0xffffffff; } var result = new byte[32]; for (var i = 0; i < H.Length; i++) { result[i * 4 + 0] = (byte)((H[i] >> (3 * 8)) & 0xff); result[i * 4 + 1] = (byte)((H[i] >> (2 * 8)) & 0xff); result[i * 4 + 2] = (byte)((H[i] >> (1 * 8)) & 0xff); result[i * 4 + 3] = (byte)((H[i] >> (0 * 8)) & 0xff); } return(result); }
private static uint σ1(uint x) { return(Sha256.ROTR(17, x) ^ Sha256.ROTR(19, x) ^ (x >> 10)); }
private static uint Σ1(uint x) { return(Sha256.ROTR(6, x) ^ Sha256.ROTR(11, x) ^ Sha256.ROTR(25, x)); }
private static uint σ0(uint x) { return(Sha256.ROTR(7, x) ^ Sha256.ROTR(18, x) ^ (x >> 3)); }
// Logical functions [§4.1.2]. private static uint Σ0(uint x) { return(Sha256.ROTR(2, x) ^ Sha256.ROTR(13, x) ^ Sha256.ROTR(22, x)); }