Files
2024-07-17 16:14:14 +08:00

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);