From ad91f35c93503a5a4736143522c842d0ed8bb07c Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Mon, 5 Dec 2022 14:12:29 +0000 Subject: [PATCH] Add SHA224 and SHA512/224 --- src/_sha2.ts | 3 ++- src/sha256.ts | 32 ++++++++++++++++++++++++-------- src/sha512.ts | 26 ++++++++++++++++++++++++++ test/hashes.test.js | 33 +++++++++++++++++++++++++++++++-- 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/src/_sha2.ts b/src/_sha2.ts index 477fdc5..15f8f63 100644 --- a/src/_sha2.ts +++ b/src/_sha2.ts @@ -90,9 +90,10 @@ export abstract class SHA2> extends Hash { this.process(view, 0); const oview = createView(out); const len = this.outputLen; + // NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT if (len % 4) throw new Error('_sha2: outputLen should be aligned to 32bit'); - const state = this.get(); const outLen = len / 4; + const state = this.get(); if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state'); for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE); } diff --git a/src/sha256.ts b/src/sha256.ts index fcf3b9d..76fdd11 100644 --- a/src/sha256.ts +++ b/src/sha256.ts @@ -32,14 +32,14 @@ const SHA256_W = new Uint32Array(64); class SHA256 extends SHA2 { // We cannot use array here since array allows indexing by variable // which means optimizer/compiler cannot use registers. - private A = IV[0] | 0; - private B = IV[1] | 0; - private C = IV[2] | 0; - private D = IV[3] | 0; - private E = IV[4] | 0; - private F = IV[5] | 0; - private G = IV[6] | 0; - private H = IV[7] | 0; + A = IV[0] | 0; + B = IV[1] | 0; + C = IV[2] | 0; + D = IV[3] | 0; + E = IV[4] | 0; + F = IV[5] | 0; + G = IV[6] | 0; + H = IV[7] | 0; constructor() { super(64, 32, 8, false); @@ -106,9 +106,25 @@ class SHA256 extends SHA2 { this.buffer.fill(0); } } +// Constants from https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf +class SHA224 extends SHA256 { + A = 0xc1059ed8 | 0; + B = 0x367cd507 | 0; + C = 0x3070dd17 | 0; + D = 0xf70e5939 | 0; + E = 0xffc00b31 | 0; + F = 0x68581511 | 0; + G = 0x64f98fa7 | 0; + H = 0xbefa4fa4 | 0; + constructor() { + super(); + this.outputLen = 28; + } +} /** * SHA2-256 hash function * @param message - data that would be hashed */ export const sha256 = wrapConstructor(() => new SHA256()); +export const sha224 = wrapConstructor(() => new SHA224()); diff --git a/src/sha512.ts b/src/sha512.ts index 1d746cf..fd6484e 100644 --- a/src/sha512.ts +++ b/src/sha512.ts @@ -166,6 +166,31 @@ export class SHA512 extends SHA2 { } } +class SHA512_224 extends SHA512 { + // h -- high 32 bits, l -- low 32 bits + Ah = 0x8c3d37c8 | 0; + Al = 0x19544da2 | 0; + Bh = 0x73e19966 | 0; + Bl = 0x89dcd4d6 | 0; + Ch = 0x1dfab7ae | 0; + Cl = 0x32ff9c82 | 0; + Dh = 0x679dd514 | 0; + Dl = 0x582f9fcf | 0; + Eh = 0x0f6d2b69 | 0; + El = 0x7bd44da8 | 0; + Fh = 0x77e36f73 | 0; + Fl = 0x04c48942 | 0; + Gh = 0x3f9d85a8 | 0; + Gl = 0x6a1d36c8 | 0; + Hh = 0x1112e6ad | 0; + Hl = 0x91d692a1 | 0; + + constructor() { + super(); + this.outputLen = 28; + } +} + class SHA512_256 extends SHA512 { // h -- high 32 bits, l -- low 32 bits Ah = 0x22312194 | 0; @@ -217,5 +242,6 @@ class SHA384 extends SHA512 { } export const sha512 = wrapConstructor(() => new SHA512()); +export const sha512_224 = wrapConstructor(() => new SHA512_224()); export const sha512_256 = wrapConstructor(() => new SHA512_256()); export const sha384 = wrapConstructor(() => new SHA384()); diff --git a/test/hashes.test.js b/test/hashes.test.js index e2656ea..b6a12d8 100644 --- a/test/hashes.test.js +++ b/test/hashes.test.js @@ -1,8 +1,8 @@ const assert = require('assert'); const { should } = require('micro-should'); const crypto = require('crypto'); -const { sha256 } = require('../sha256'); -const { sha384, sha512, sha512_256 } = require('../sha512'); +const { sha224, sha256 } = require('../sha256'); +const { sha384, sha512, sha512_224, sha512_256 } = require('../sha512'); const { sha3_224, sha3_256, @@ -64,6 +64,20 @@ const HASHES = { '7789f0c9 ef7bfc40 d9331114 3dfbe69e 2017f592', ], }, + SHA224: { + fn: sha224, + obj: sha224.create, + node: (buf) => Uint8Array.from(crypto.createHash('sha224').update(buf).digest()), + node_obj: () => crypto.createHash('sha224'), + nist: [ + '23097d22 3405d822 8642a477 bda255b3 2aadbce4 bda0b3f7 e36c9da7', + 'd14a028c 2a3a2bc9 476102bb 288234c4 15a2b01f 828ea62a c5b3e42f', + '75388b16 512776cc 5dba5da1 fd890150 b0c6455c b4f58b19 52522525', + 'c97ca9a5 59850ce9 7a04a96d ef6d99a9 e0e0e2ab 14e6b8df 265fc0b3', + '20794655 980c91d8 bbb4c1ea 97618a4b f03f4258 1948b2ee 4ee7ad67', + 'b5989713 ca4fe47a 009f8621 980b34e6 d63ed306 3b2a0a2c 867d8a85', + ], + }, SHA256: { fn: sha256, obj: sha256.create, @@ -120,6 +134,21 @@ const HASHES = { '5441235cc0235341 ed806a64fb354742 b5e5c02a3c5cb71b 5f63fb793458d8fd ae599c8cd8884943 c04f11b31b89f023', ], }, + SHA512_224: { + fn: sha512_224, + obj: sha512_224.create, + node: (buf) => Uint8Array.from(crypto.createHash('sha512-224').update(buf).digest()), + node_obj: () => crypto.createHash('sha512-224'), + // There is no official vectors, so we created them via: + // > NIST_VECTORS.map((i) => crypto.createHash('sha512-256').update(i[2]).digest('hex')) + nist: [ + '4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa', + '6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4', + 'e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174', + '23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9', + '37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287', + ], + }, SHA512_256: { fn: sha512_256, obj: sha512_256.create,