296 lines
7.4 KiB
JavaScript
296 lines
7.4 KiB
JavaScript
import {
|
|
WordArray,
|
|
Hasher,
|
|
} from './core.js';
|
|
import { X64Word } from './x64-core.js';
|
|
|
|
// Constants tables
|
|
const RHO_OFFSETS = [];
|
|
const PI_INDEXES = [];
|
|
const ROUND_CONSTANTS = [];
|
|
|
|
// Compute Constants
|
|
// Compute rho offset constants
|
|
let _x = 1;
|
|
let _y = 0;
|
|
for (let t = 0; t < 24; t += 1) {
|
|
RHO_OFFSETS[_x + 5 * _y] = ((t + 1) * (t + 2) / 2) % 64;
|
|
|
|
const newX = _y % 5;
|
|
const newY = (2 * _x + 3 * _y) % 5;
|
|
_x = newX;
|
|
_y = newY;
|
|
}
|
|
|
|
// Compute pi index constants
|
|
for (let x = 0; x < 5; x += 1) {
|
|
for (let y = 0; y < 5; y += 1) {
|
|
PI_INDEXES[x + 5 * y] = y + ((2 * x + 3 * y) % 5) * 5;
|
|
}
|
|
}
|
|
|
|
// Compute round constants
|
|
let LFSR = 0x01;
|
|
for (let i = 0; i < 24; i += 1) {
|
|
let roundConstantMsw = 0;
|
|
let roundConstantLsw = 0;
|
|
|
|
for (let j = 0; j < 7; j += 1) {
|
|
if (LFSR & 0x01) {
|
|
const bitPosition = (1 << j) - 1;
|
|
if (bitPosition < 32) {
|
|
roundConstantLsw ^= 1 << bitPosition;
|
|
} else /* if (bitPosition >= 32) */ {
|
|
roundConstantMsw ^= 1 << (bitPosition - 32);
|
|
}
|
|
}
|
|
|
|
// Compute next LFSR
|
|
if (LFSR & 0x80) {
|
|
// Primitive polynomial over GF(2): x^8 + x^6 + x^5 + x^4 + 1
|
|
LFSR = (LFSR << 1) ^ 0x71;
|
|
} else {
|
|
LFSR <<= 1;
|
|
}
|
|
}
|
|
|
|
ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw);
|
|
}
|
|
|
|
// Reusable objects for temporary values
|
|
const T = [];
|
|
for (let i = 0; i < 25; i += 1) {
|
|
T[i] = X64Word.create();
|
|
}
|
|
|
|
/**
|
|
* SHA-3 hash algorithm.
|
|
*/
|
|
export class SHA3Algo extends Hasher {
|
|
constructor(cfg) {
|
|
/**
|
|
* Configuration options.
|
|
*
|
|
* @property {number} outputLength
|
|
* The desired number of bits in the output hash.
|
|
* Only values permitted are: 224, 256, 384, 512.
|
|
* Default: 512
|
|
*/
|
|
super(Object.assign(
|
|
{ outputLength: 512 },
|
|
cfg,
|
|
));
|
|
}
|
|
|
|
_doReset() {
|
|
this._state = [];
|
|
const state = this._state;
|
|
for (let i = 0; i < 25; i += 1) {
|
|
state[i] = new X64Word();
|
|
}
|
|
|
|
this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32;
|
|
}
|
|
|
|
_doProcessBlock(M, offset) {
|
|
// Shortcuts
|
|
const state = this._state;
|
|
const nBlockSizeLanes = this.blockSize / 2;
|
|
|
|
// Absorb
|
|
for (let i = 0; i < nBlockSizeLanes; i += 1) {
|
|
// Shortcuts
|
|
let M2i = M[offset + 2 * i];
|
|
let M2i1 = M[offset + 2 * i + 1];
|
|
|
|
// Swap endian
|
|
M2i = (((M2i << 8) | (M2i >>> 24)) & 0x00ff00ff)
|
|
| (((M2i << 24) | (M2i >>> 8)) & 0xff00ff00);
|
|
M2i1 = (((M2i1 << 8) | (M2i1 >>> 24)) & 0x00ff00ff)
|
|
| (((M2i1 << 24) | (M2i1 >>> 8)) & 0xff00ff00);
|
|
|
|
// Absorb message into state
|
|
const lane = state[i];
|
|
lane.high ^= M2i1;
|
|
lane.low ^= M2i;
|
|
}
|
|
|
|
// Rounds
|
|
for (let round = 0; round < 24; round += 1) {
|
|
// Theta
|
|
for (let x = 0; x < 5; x += 1) {
|
|
// Mix column lanes
|
|
let tMsw = 0;
|
|
let tLsw = 0;
|
|
for (let y = 0; y < 5; y += 1) {
|
|
const lane = state[x + 5 * y];
|
|
tMsw ^= lane.high;
|
|
tLsw ^= lane.low;
|
|
}
|
|
|
|
// Temporary values
|
|
const Tx = T[x];
|
|
Tx.high = tMsw;
|
|
Tx.low = tLsw;
|
|
}
|
|
for (let x = 0; x < 5; x += 1) {
|
|
// Shortcuts
|
|
const Tx4 = T[(x + 4) % 5];
|
|
const Tx1 = T[(x + 1) % 5];
|
|
const Tx1Msw = Tx1.high;
|
|
const Tx1Lsw = Tx1.low;
|
|
|
|
// Mix surrounding columns
|
|
const tMsw = Tx4.high ^ ((Tx1Msw << 1) | (Tx1Lsw >>> 31));
|
|
const tLsw = Tx4.low ^ ((Tx1Lsw << 1) | (Tx1Msw >>> 31));
|
|
for (let y = 0; y < 5; y += 1) {
|
|
const lane = state[x + 5 * y];
|
|
lane.high ^= tMsw;
|
|
lane.low ^= tLsw;
|
|
}
|
|
}
|
|
|
|
// Rho Pi
|
|
for (let laneIndex = 1; laneIndex < 25; laneIndex += 1) {
|
|
let tMsw;
|
|
let tLsw;
|
|
|
|
// Shortcuts
|
|
const lane = state[laneIndex];
|
|
const laneMsw = lane.high;
|
|
const laneLsw = lane.low;
|
|
const rhoOffset = RHO_OFFSETS[laneIndex];
|
|
|
|
// Rotate lanes
|
|
if (rhoOffset < 32) {
|
|
tMsw = (laneMsw << rhoOffset) | (laneLsw >>> (32 - rhoOffset));
|
|
tLsw = (laneLsw << rhoOffset) | (laneMsw >>> (32 - rhoOffset));
|
|
} else /* if (rhoOffset >= 32) */ {
|
|
tMsw = (laneLsw << (rhoOffset - 32)) | (laneMsw >>> (64 - rhoOffset));
|
|
tLsw = (laneMsw << (rhoOffset - 32)) | (laneLsw >>> (64 - rhoOffset));
|
|
}
|
|
|
|
// Transpose lanes
|
|
const TPiLane = T[PI_INDEXES[laneIndex]];
|
|
TPiLane.high = tMsw;
|
|
TPiLane.low = tLsw;
|
|
}
|
|
|
|
// Rho pi at x = y = 0
|
|
const T0 = T[0];
|
|
const state0 = state[0];
|
|
T0.high = state0.high;
|
|
T0.low = state0.low;
|
|
|
|
// Chi
|
|
for (let x = 0; x < 5; x += 1) {
|
|
for (let y = 0; y < 5; y += 1) {
|
|
// Shortcuts
|
|
const laneIndex = x + 5 * y;
|
|
const lane = state[laneIndex];
|
|
const TLane = T[laneIndex];
|
|
const Tx1Lane = T[((x + 1) % 5) + 5 * y];
|
|
const Tx2Lane = T[((x + 2) % 5) + 5 * y];
|
|
|
|
// Mix rows
|
|
lane.high = TLane.high ^ (~Tx1Lane.high & Tx2Lane.high);
|
|
lane.low = TLane.low ^ (~Tx1Lane.low & Tx2Lane.low);
|
|
}
|
|
}
|
|
|
|
// Iota
|
|
const lane = state[0];
|
|
const roundConstant = ROUND_CONSTANTS[round];
|
|
lane.high ^= roundConstant.high;
|
|
lane.low ^= roundConstant.low;
|
|
}
|
|
}
|
|
|
|
_doFinalize() {
|
|
// Shortcuts
|
|
const data = this._data;
|
|
const dataWords = data.words;
|
|
const nBitsLeft = data.sigBytes * 8;
|
|
const blockSizeBits = this.blockSize * 32;
|
|
|
|
// Add padding
|
|
dataWords[nBitsLeft >>> 5] |= 0x1 << (24 - (nBitsLeft % 32));
|
|
dataWords[((Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits) >>> 5) - 1] |= 0x80;
|
|
data.sigBytes = dataWords.length * 4;
|
|
|
|
// Hash final blocks
|
|
this._process();
|
|
|
|
// Shortcuts
|
|
const state = this._state;
|
|
const outputLengthBytes = this.cfg.outputLength / 8;
|
|
const outputLengthLanes = outputLengthBytes / 8;
|
|
|
|
// Squeeze
|
|
const hashWords = [];
|
|
for (let i = 0; i < outputLengthLanes; i += 1) {
|
|
// Shortcuts
|
|
const lane = state[i];
|
|
let laneMsw = lane.high;
|
|
let laneLsw = lane.low;
|
|
|
|
// Swap endian
|
|
laneMsw = (((laneMsw << 8) | (laneMsw >>> 24)) & 0x00ff00ff)
|
|
| (((laneMsw << 24) | (laneMsw >>> 8)) & 0xff00ff00);
|
|
laneLsw = (((laneLsw << 8) | (laneLsw >>> 24)) & 0x00ff00ff)
|
|
| (((laneLsw << 24) | (laneLsw >>> 8)) & 0xff00ff00);
|
|
|
|
// Squeeze state to retrieve hash
|
|
hashWords.push(laneLsw);
|
|
hashWords.push(laneMsw);
|
|
}
|
|
|
|
// Return final computed hash
|
|
return new WordArray(hashWords, outputLengthBytes);
|
|
}
|
|
|
|
clone() {
|
|
const clone = super.clone.call(this);
|
|
|
|
clone._state = this._state.slice(0);
|
|
const state = clone._state;
|
|
for (let i = 0; i < 25; i += 1) {
|
|
state[i] = state[i].clone();
|
|
}
|
|
|
|
return clone;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shortcut function to the hasher's object interface.
|
|
*
|
|
* @param {WordArray|string} message The message to hash.
|
|
*
|
|
* @return {WordArray} The hash.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var hash = CryptoJS.SHA3('message');
|
|
* var hash = CryptoJS.SHA3(wordArray);
|
|
*/
|
|
export const SHA3 = Hasher._createHelper(SHA3Algo);
|
|
|
|
/**
|
|
* Shortcut function to the HMAC's object interface.
|
|
*
|
|
* @param {WordArray|string} message The message to hash.
|
|
* @param {WordArray|string} key The secret key.
|
|
*
|
|
* @return {WordArray} The HMAC.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var hmac = CryptoJS.HmacSHA3(message, key);
|
|
*/
|
|
export const HmacSHA3 = Hasher._createHmacHelper(SHA3Algo);
|