Last active
November 2, 2017 02:58
-
-
Save bellbind/de02a751e52ab3a3d35e843221d3d37d to your computer and use it in GitHub Desktop.
[emscripten][WebAssembly]SHA256 implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
const fs = require("fs"); | |
const names = process.argv.slice(2); | |
names.forEach(name => { | |
const hash = require("crypto").createHash("sha256"); | |
const rs = fs.createReadStream(name); | |
rs.on("readable", _ => { | |
const buf = rs.read(); | |
if (buf) hash.update(buf); | |
}); | |
rs.on("end", _ => { | |
console.log( | |
`${Buffer.from(hash.digest()).toString("hex")} ${name}`); | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "sha256em", | |
"version": "1.0.0", | |
"main": "sha256mod.js", | |
"bin": "sha256main.js", | |
"scripts": { | |
"preinstall": "echo 'NOTICE: this module required emscripten and binaryen'", | |
"install": "emcc -std=c11 -pedantic -O3 -funroll-loops -Wall -Wextra sha256extracted.c -o sha256.js -s WASM=1 -s EXPORTED_FUNCTIONS='[\"_sha256_init\",\"_sha256_update\",\"_sha256_digest\"]'" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __SHA256_H__ | |
#define __SHA256_H__ | |
#include <stdint.h> | |
#define SHA256_HASH_BYTES 32 | |
struct sha256 { | |
uint32_t h[8]; | |
uint64_t bytes; | |
unsigned char chunk[64]; | |
long chunk_size; | |
}; | |
extern void sha256_init(struct sha256* self); | |
extern void sha256_update(struct sha256* self, const void* buf, size_t bytes); | |
extern void sha256_digest(struct sha256* self, void* digest); | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <string.h> | |
#include "sha256.h" | |
// see: https://en.wikipedia.org/wiki/SHA-2#Pseudocode | |
static const uint32_t K[] = { | |
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, | |
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, | |
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, | |
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, | |
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, | |
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, | |
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, | |
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, | |
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, | |
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, | |
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, | |
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | |
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, | |
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 | |
}; | |
static const uint32_t H[] = { | |
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, | |
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 | |
}; | |
static inline uint32_t big32(uint32_t v) { | |
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | |
return v; | |
#else | |
union { | |
struct {uint8_t b0, b1, b2, b3;}; | |
uint32_t v; | |
} le = {.v = v}; | |
union { | |
struct {uint8_t b3, b2, b1, b0;}; | |
uint32_t v; | |
} be = {.b0 = le.b0, .b1 = le.b1, .b2 = le.b2, .b3 = le.b3}; | |
return be.v; | |
#endif | |
} | |
static inline uint64_t big64(uint64_t v) { | |
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | |
return v; | |
#else | |
union { | |
struct {uint8_t b0, b1, b2, b3, b4, b5, b6, b7;}; | |
uint64_t v; | |
} le = {.v = v}; | |
union { | |
struct {uint8_t b7, b6, b5, b4, b3, b2, b1, b0;}; | |
uint64_t v; | |
} be = { | |
.b0 = le.b0, .b1 = le.b1, .b2 = le.b2, .b3 = le.b3, | |
.b4 = le.b4, .b5 = le.b5, .b6 = le.b6, .b7 = le.b7, | |
}; | |
return be.v; | |
#endif | |
} | |
static inline uint32_t rotr(uint32_t v, size_t n) { | |
return (v >> n) | (v << (32 - n)); | |
} | |
static void update(struct sha256* self); | |
static void tail(struct sha256* self); | |
extern void sha256_init(struct sha256* self) { | |
memcpy(self->h, H, sizeof H); | |
self->bytes = 0; | |
self->chunk_size = 0; | |
} | |
extern void sha256_update(struct sha256* self, const void* buf, size_t bytes) { | |
if (self->chunk_size < 0) return; | |
const char* cur = buf; | |
unsigned char* chunk = self->chunk; | |
size_t chunk_size = self->chunk_size; | |
self->bytes += bytes; | |
const char* end = &cur[bytes]; | |
for (const char* next = &cur[64 - chunk_size]; next <= end; next += 64) { | |
memcpy(&chunk[chunk_size], cur, next - cur); | |
update(self); | |
cur = next; | |
chunk_size = 0; | |
} | |
size_t rest = end - cur; | |
memcpy(&chunk[chunk_size], cur, rest); | |
self->chunk_size = chunk_size + rest; | |
} | |
extern void sha256_digest(struct sha256* self, void* digest) { | |
if (self->chunk_size >= 0) { | |
tail(self); | |
self->chunk_size = -1; | |
} | |
uint32_t* buf = (uint32_t*) digest; | |
for (int i = 0; i < 8; i++) { | |
buf[i] = big32(self->h[i]); | |
} | |
} | |
static inline uint32_t s0(uint32_t a) { | |
return rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22); | |
} | |
static inline uint32_t s1(uint32_t e) { | |
return rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25); | |
} | |
static inline uint32_t ch(uint32_t e, uint32_t f, uint32_t g) { | |
return (e & f) ^ (~e & g); | |
} | |
static inline uint32_t maj(uint32_t a, uint32_t b, uint32_t c) { | |
return (a & b) ^ (a & c) ^ (b & c); | |
} | |
static inline uint32_t | |
t1(uint32_t e, uint32_t f, uint32_t g, uint32_t w, uint32_t k) { | |
return s1(e) + ch(e, f, g) + k + w; | |
} | |
static inline uint32_t t2(uint32_t a, uint32_t b, uint32_t c) { | |
return s0(a) + maj(a, b, c); | |
} | |
static inline void | |
step(uint32_t a, uint32_t b, uint32_t c, uint32_t* d, | |
uint32_t e, uint32_t f, uint32_t g, uint32_t* h, uint32_t w, uint32_t k) { | |
*h += t1(e, f, g, w, k); | |
*d += *h; | |
*h += t2(a, b, c); | |
} | |
static void update(struct sha256* self) { | |
uint32_t* sh = self->h; | |
uint32_t* cd = (uint32_t*) self->chunk; | |
uint32_t w[64]; | |
#pragma unroll | |
for (size_t i = 0; i < 16; i++) { | |
w[i] = big32(cd[i]); | |
} | |
#pragma unroll | |
for (size_t i = 16; i < 64; i++) { | |
uint32_t w16 = w[i - 16], w15 = w[i - 15], w7 = w[i - 7], w2 = w[i - 2]; | |
uint32_t s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >> 3); | |
uint32_t s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >> 10); | |
w[i] = w16 + s0 + w7 + s1; | |
} | |
uint32_t a = sh[0], b = sh[1], c = sh[2], d = sh[3], | |
e = sh[4], f = sh[5], g = sh[6], h = sh[7]; | |
/* | |
for (int i = 0; i < 64; i++) { | |
uint32_t s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22); | |
uint32_t s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25); | |
uint32_t ch = (e & f) ^ (~e & g); | |
uint32_t maj = (a & b) ^ (a & c) ^ (b & c); | |
uint32_t t1 = h + s1 + ch + K[i] + w[i]; | |
uint32_t t2 = s0 + maj; | |
h = g; g = f; f = e; e = d + t1; | |
d = c; c = b; b = a; a = t1 + t2; | |
} | |
*/ | |
// extract whole loop for speed: Instead of variable rotation, | |
// Rotating parameters for avoiding assignment, | |
// In each step, updated only `d` and `h` | |
step(a, b, c, &d, e, f, g, &h, w[0], K[0]); | |
step(h, a, b, &c, d, e, f, &g, w[1], K[1]); | |
step(g, h, a, &b, c, d, e, &f, w[2], K[2]); | |
step(f, g, h, &a, b, c, d, &e, w[3], K[3]); | |
step(e, f, g, &h, a, b, c, &d, w[4], K[4]); | |
step(d, e, f, &g, h, a, b, &c, w[5], K[5]); | |
step(c, d, e, &f, g, h, a, &b, w[6], K[6]); | |
step(b, c, d, &e, f, g, h, &a, w[7], K[7]); | |
step(a, b, c, &d, e, f, g, &h, w[8], K[8]); | |
step(h, a, b, &c, d, e, f, &g, w[9], K[9]); | |
step(g, h, a, &b, c, d, e, &f, w[10], K[10]); | |
step(f, g, h, &a, b, c, d, &e, w[11], K[11]); | |
step(e, f, g, &h, a, b, c, &d, w[12], K[12]); | |
step(d, e, f, &g, h, a, b, &c, w[13], K[13]); | |
step(c, d, e, &f, g, h, a, &b, w[14], K[14]); | |
step(b, c, d, &e, f, g, h, &a, w[15], K[15]); | |
step(a, b, c, &d, e, f, g, &h, w[16], K[16]); | |
step(h, a, b, &c, d, e, f, &g, w[17], K[17]); | |
step(g, h, a, &b, c, d, e, &f, w[18], K[18]); | |
step(f, g, h, &a, b, c, d, &e, w[19], K[19]); | |
step(e, f, g, &h, a, b, c, &d, w[20], K[20]); | |
step(d, e, f, &g, h, a, b, &c, w[21], K[21]); | |
step(c, d, e, &f, g, h, a, &b, w[22], K[22]); | |
step(b, c, d, &e, f, g, h, &a, w[23], K[23]); | |
step(a, b, c, &d, e, f, g, &h, w[24], K[24]); | |
step(h, a, b, &c, d, e, f, &g, w[25], K[25]); | |
step(g, h, a, &b, c, d, e, &f, w[26], K[26]); | |
step(f, g, h, &a, b, c, d, &e, w[27], K[27]); | |
step(e, f, g, &h, a, b, c, &d, w[28], K[28]); | |
step(d, e, f, &g, h, a, b, &c, w[29], K[29]); | |
step(c, d, e, &f, g, h, a, &b, w[30], K[30]); | |
step(b, c, d, &e, f, g, h, &a, w[31], K[31]); | |
step(a, b, c, &d, e, f, g, &h, w[32], K[32]); | |
step(h, a, b, &c, d, e, f, &g, w[33], K[33]); | |
step(g, h, a, &b, c, d, e, &f, w[34], K[34]); | |
step(f, g, h, &a, b, c, d, &e, w[35], K[35]); | |
step(e, f, g, &h, a, b, c, &d, w[36], K[36]); | |
step(d, e, f, &g, h, a, b, &c, w[37], K[37]); | |
step(c, d, e, &f, g, h, a, &b, w[38], K[38]); | |
step(b, c, d, &e, f, g, h, &a, w[39], K[39]); | |
step(a, b, c, &d, e, f, g, &h, w[40], K[40]); | |
step(h, a, b, &c, d, e, f, &g, w[41], K[41]); | |
step(g, h, a, &b, c, d, e, &f, w[42], K[42]); | |
step(f, g, h, &a, b, c, d, &e, w[43], K[43]); | |
step(e, f, g, &h, a, b, c, &d, w[44], K[44]); | |
step(d, e, f, &g, h, a, b, &c, w[45], K[45]); | |
step(c, d, e, &f, g, h, a, &b, w[46], K[46]); | |
step(b, c, d, &e, f, g, h, &a, w[47], K[47]); | |
step(a, b, c, &d, e, f, g, &h, w[48], K[48]); | |
step(h, a, b, &c, d, e, f, &g, w[49], K[49]); | |
step(g, h, a, &b, c, d, e, &f, w[50], K[50]); | |
step(f, g, h, &a, b, c, d, &e, w[51], K[51]); | |
step(e, f, g, &h, a, b, c, &d, w[52], K[52]); | |
step(d, e, f, &g, h, a, b, &c, w[53], K[53]); | |
step(c, d, e, &f, g, h, a, &b, w[54], K[54]); | |
step(b, c, d, &e, f, g, h, &a, w[55], K[55]); | |
step(a, b, c, &d, e, f, g, &h, w[56], K[56]); | |
step(h, a, b, &c, d, e, f, &g, w[57], K[57]); | |
step(g, h, a, &b, c, d, e, &f, w[58], K[58]); | |
step(f, g, h, &a, b, c, d, &e, w[59], K[59]); | |
step(e, f, g, &h, a, b, c, &d, w[60], K[60]); | |
step(d, e, f, &g, h, a, b, &c, w[61], K[61]); | |
step(c, d, e, &f, g, h, a, &b, w[62], K[62]); | |
step(b, c, d, &e, f, g, h, &a, w[63], K[63]); | |
sh[0] += a; sh[1] += b; sh[2] += c; sh[3] += d; | |
sh[4] += e; sh[5] += f; sh[6] += g; sh[7] += h; | |
} | |
static void tail(struct sha256* self) { | |
uint64_t bits = big64(self->bytes * 8); | |
self->chunk[self->chunk_size] = 0x80; | |
memset(&self->chunk[self->chunk_size + 1], 0, 63 - self->chunk_size); | |
if (self->chunk_size + 1 + sizeof bits > 64) { | |
update(self); | |
memset(self->chunk, 0, 64 - sizeof bits); | |
} | |
memcpy(&self->chunk[64 - sizeof bits], &bits, sizeof bits); | |
update(self); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
"use strict"; | |
const fs = require("fs"); | |
// $ emcc -std=c11 -pedantic -O3 -Wall -Wextra sha256.c -o sha256.js -s WASM=1 | |
// -s EXPORTED_FUNCTIONS='["_sha256_init","_sha256_update","_sha256_digest"]' | |
const Hash = require("./sha256mod"); | |
function main() { | |
if (process.argv[2] === "-c") { | |
const names = process.argv.slice(3); | |
check(names).then(({fails, sumerror}) => { | |
if (fails > 0) { | |
console.error(`${process.argv[1]}: WARNING: ${fails | |
} computed checksum${fails > 1 ? "s" : ""} did NOT match`); | |
} | |
process.exit(fails > 0 || sumerror > 0 ? 1 : 0); | |
}).catch(err => { | |
console.error(err); | |
process.exit(1); | |
}); | |
} else { | |
const names = process.argv.slice(2); | |
generate(names).then(_ => process.exit(0)).catch(err => { | |
console.error(err); | |
process.exit(1); | |
}); | |
} | |
} | |
Hash.boot(main); | |
function digestFile(name) { | |
return new Promise((resolve, reject) => { | |
const h = new Hash(); | |
const bufsize = 64 * 1024; | |
const rs = fs.createReadStream(name); | |
rs.on("readable", _ => { | |
let chunk; | |
while ((chunk = rs.read(bufsize)) !== null) { | |
h.update(chunk); | |
} | |
}); | |
rs.on("end", _ => { | |
resolve(h.digest()); | |
}); | |
rs.on("error", error => { | |
reject(error); | |
h.close(); | |
}); | |
}); | |
} | |
async function generate(names) { | |
for (const name of names) { | |
const digest = await digestFile(name); | |
console.log(`${digest.toString("hex")} ${name}`); | |
} | |
} | |
async function check(names) { | |
let fails = 0, sumerror = 0; | |
for (const name of names) { | |
try { | |
const r = await checkFile(name); | |
fails += r.fails; | |
} catch (err) { | |
sumerror++; | |
console.error(`${process.argv[1]}: ${name | |
}: No such file or directory`); | |
} | |
} | |
return {fails, sumerror}; | |
} | |
async function checkFile(name) { | |
const sumReg = /^\s*([\da-fA-F]{64})[\t ][ ]?(.+)$/; | |
let fails = 0; | |
for (const sum of fs.readFileSync(name, "utf8").split(/\n/)) { | |
const match = sum.match(sumReg); | |
if (!match) continue; | |
const expected = Buffer.from(match[1], "hex"); | |
try { | |
const digest = await digestFile(match[2]); | |
if (digest.compare(expected) === 0) { | |
console.log(`${match[2]}: OK`); | |
} else { | |
fails++; | |
console.log(`${match[2]}: FAILED`); | |
} | |
} catch (err) { | |
fails++; | |
console.log(`${match[2]}: FAILED open or read`); | |
} | |
} | |
return {fails}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
const fs = require("fs"); | |
// $ emcc -std=c11 -pedantic -O3 -Wall -Wextra sha256.c -o sha256.js -s WASM=1 | |
// -s EXPORTED_FUNCTIONS='["_sha256_init","_sha256_update","_sha256_digest"]' | |
const cwd = process.cwd(); | |
process.chdir(__dirname); | |
const sha256 = require("./sha256"); | |
let initfuncs = []; | |
if (sha256.calledRun) process.chdir(cwd); | |
else sha256.onRuntimeInitialized = function () { | |
process.chdir(cwd); | |
initfuncs.forEach(initfunc => { | |
try { | |
initfunc(); | |
} catch (err) { | |
console.error(err); | |
} | |
}); | |
initfuncs = []; | |
}; | |
class Hash { | |
constructor() { | |
// sizeof (struct sha256) = 112 (align 8) | |
const pctx = sha256._malloc(112); | |
const hashsize = 32; | |
const phash = sha256._malloc(hashsize); | |
const hash = new Uint8Array(sha256.buffer, phash, hashsize); | |
const bufsiz = 64 * 1024; | |
const pbuffer = sha256._malloc(bufsiz); | |
const buffer = new Uint8Array(sha256.buffer, pbuffer, bufsiz); | |
this.pctx = pctx; | |
this.buffer = buffer; | |
this.hash = hash; | |
this.closed = false; | |
sha256._sha256_init(pctx); | |
} | |
update(chunk) { | |
if (this.closed) throw Error("already closed"); | |
if (this.buffer.byteLength < chunk.length) { | |
let bufsiz = this.buffer.byteLength * 2; | |
while (bufsiz < chunk.length) bufsiz *= 2; | |
sha256._free(this.buffer.byteOffset); | |
const pbuffer = sha256._malloc(bufsiz); | |
this.buffer = new Uint8Array(sha256.buffer, pbuffer, bufsiz); | |
} | |
this.buffer.set(chunk); | |
sha256._sha256_update(this.pctx, this.buffer.byteOffset, chunk.length); | |
return this; | |
} | |
digest() { | |
if (this.closed) throw Error("already closed"); | |
sha256._sha256_digest(this.pctx, this.hash.byteOffset); | |
const r = Buffer.from(this.hash); | |
this.close(); | |
return r; | |
} | |
close() { | |
if (this.closed) return; | |
sha256._free(this.pctx); | |
sha256._free(this.buffer.byteOffset); | |
sha256._free(this.hash.byteOffset); | |
this.closed = true; | |
} | |
static boot(initfunc) { | |
if (sha256.calledRun) initfunc(); | |
else initfuncs.push(initfunc); | |
} | |
} | |
module.exports = Hash; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include "sha256.h" | |
// [sizeof value checker] | |
// $ emcc -O3 -std=c11 -pedantic -Wall -Wextra sizeof.c -o sizeof.js -s WASM=1 | |
// $ node sizeof.js | |
#define show_size(type) printf("sizeof (" #type ") = %zu\n", sizeof (type)) | |
#define show_offset(type, member) {\ | |
type *d = NULL;\ | |
printf("offset " #type "." #member " = %zu\n", \ | |
(void*) &d->member - (void*) d);\ | |
} | |
int main(int argc, char** argv) { | |
(void) argc, (void) argv; | |
show_size(size_t); | |
show_size(struct sha256); | |
show_offset(struct sha256, h); | |
show_offset(struct sha256, bytes); | |
show_offset(struct sha256, chunk); | |
show_offset(struct sha256, chunk_size); | |
return 0; | |
} |
When emscripten and binaryen well set up, with npx:
$ npx https://gist.github.com/bellbind/de02a751e52ab3a3d35e843221d3d37d /bin/bash |tee sum
295fbc2356e8605e804f95cb6d6f992335e247dbf11767fe8781e2a7f889978a /bin/bash
$ npx https://gist.github.com/bellbind/de02a751e52ab3a3d35e843221d3d37d -c sum
/bin/bash: OK
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Execution times