Skip to content

Commit

Permalink
[sha256] add node native fallback
Browse files Browse the repository at this point in the history
  • Loading branch information
dmonad committed Jul 11, 2023
1 parent 29b9986 commit 6a18123
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 48 deletions.
79 changes: 33 additions & 46 deletions hash/sha256.js → hash/sha256.fallback.js
Expand Up @@ -8,6 +8,36 @@
import * as binary from '../binary.js'
import * as math from '../math.js'

/**
* @param {number} w - a 32bit uint
* @param {number} shift
*/
const rotr = (w, shift) => (w >>> shift) | (w << (32 - shift))

/**
* Helper for SHA-224 & SHA-256. See 4.1.2.
* @param {number} x
*/
const sum0to256 = x => rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22)

/**
* Helper for SHA-224 & SHA-256. See 4.1.2.
* @param {number} x
*/
const sum1to256 = x => rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25)

/**
* Helper for SHA-224 & SHA-256. See 4.1.2.
* @param {number} x
*/
const sigma0to256 = x => rotr(x, 7) ^ rotr(x, 18) ^ x >>> 3

/**
* Helper for SHA-224 & SHA-256. See 4.1.2.
* @param {number} x
*/
const sigma1to256 = x => rotr(x, 17) ^ rotr(x, 19) ^ x >>> 10

// @todo don't init these variables globally

/**
Expand All @@ -26,6 +56,7 @@ const K = new Uint32Array([
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
])

/**
* See 5.3.3. Initial hash value.
*
Expand Down Expand Up @@ -111,8 +142,8 @@ const updateHash = (H, W, K) => {
let h = H[7]
// Step 3
for (let t = 0; t < 64; t++) {
const T1 = (h + sum1to256(e) + ch(e, f, g) + K[t] + W[t]) >>> 0
const T2 = (sum0to256(a) + maj(a, b, c)) >>> 0
const T1 = (h + sum1to256(e) + ((e & f) ^ (~e & g)) + K[t] + W[t]) >>> 0
const T2 = (sum0to256(a) + ((a & b) ^ (a & c) ^ (b & c))) >>> 0
h = g
g = f
f = e
Expand All @@ -131,47 +162,3 @@ const updateHash = (H, W, K) => {
H[6] += g
H[7] += h
}

/**
* @param {number} x
* @param {number} y
* @param {number} z
*/
const ch = (x, y, z) => (x & y) ^ (~x & z)

/**
* @param {number} x
* @param {number} y
* @param {number} z
*/
const maj = (x, y, z) => (x & y) ^ (x & z) ^ (y & z)

/**
* @param {number} w - a 32bit uint
* @param {number} shift
*/
const rotr = (w, shift) => (w >>> shift) | (w << (32 - shift))

/**
* Helper for SHA-224 & SHA-256. See 4.1.2.
* @param {number} x
*/
const sum0to256 = x => rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22)

/**
* Helper for SHA-224 & SHA-256. See 4.1.2.
* @param {number} x
*/
const sum1to256 = x => rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25)

/**
* Helper for SHA-224 & SHA-256. See 4.1.2.
* @param {number} x
*/
const sigma0to256 = x => rotr(x, 7) ^ rotr(x, 18) ^ x >>> 3

/**
* Helper for SHA-224 & SHA-256. See 4.1.2.
* @param {number} x
*/
const sigma1to256 = x => rotr(x, 17) ^ rotr(x, 19) ^ x >>> 10
10 changes: 10 additions & 0 deletions hash/sha256.node.js
@@ -0,0 +1,10 @@
import { createHash } from 'node:crypto'

/**
* @param {Uint8Array} data
*/
export const hash = data => {
const hasher = createHash('sha256')
hasher.update(data)
return hasher.digest()
}
14 changes: 12 additions & 2 deletions hash/sha256.test.js
@@ -1,10 +1,11 @@
import * as t from '../testing.js'
import * as sha256 from './sha256.js'
import * as sha256 from './sha256.fallback.js'
import * as buffer from '../buffer.js'
import * as string from '../string.js'
import * as prng from '../prng.js'
import * as webcrypto from 'lib0/webcrypto'
import * as promise from '../promise.js'
import * as env from '../environment.js'

/**
* @param {t.TestCase} _tc
Expand Down Expand Up @@ -56,12 +57,21 @@ export const testBenchmarkSha256 = async _tc => {
webcrypto.getRandomValues(data)
datas.push(data)
}
t.measureTime(`[lib0] Time to hash ${N} random values of size ${BS}`, () => {
t.measureTime(`[lib0 (fallback))] Time to hash ${N} random values of size ${BS}`, () => {
for (let i = 0; i < N; i++) {
const x = sha256.hash(datas[i])
if (x === null) throw new Error()
}
})
if (env.isNode) {
const nodeSha = await import('./sha256.node.js')
t.measureTime(`[lib0 (node))] Time to hash ${N} random values of size ${BS}`, () => {
for (let i = 0; i < N; i++) {
const x = nodeSha.hash(datas[i])
if (x === null) throw new Error()
}
})
}
t.measureTime(`[webcrypto sequentially] Time to hash ${N} random values of size ${BS}`, async () => {
for (let i = 0; i < N; i++) {
const x = await webcrypto.subtle.digest('SHA-256', datas[i])
Expand Down
12 changes: 12 additions & 0 deletions package.json
Expand Up @@ -103,6 +103,18 @@
"import": "./hash/rabin.js",
"require": "./dist/rabin.cjs"
},
"./hash/sha256": {
"types": "./hash/sha256.fallback.d.ts",
"node": {
"require": "./dist/sha256.node.cjs",
"default": "./hash/sha256.node.js"
},
"default": {
"module": "./hash/sha256.fallback.js"
"require": "./dist/sha256.fallback.cjs",
"default": "./hash/sha256.fallback.js"
}
},
"./decoding.js": "./decoding.js",
"./dist/decoding.cjs": "./dist/decoding.cjs",
"./decoding": {
Expand Down

0 comments on commit 6a18123

Please sign in to comment.