Skip to content

Commit

Permalink
feat: keccak
Browse files Browse the repository at this point in the history
  • Loading branch information
AbigailDeng authored and AbigailDeng committed Mar 17, 2023
1 parent 5978c6e commit af44292
Show file tree
Hide file tree
Showing 10 changed files with 415 additions and 13 deletions.
49 changes: 49 additions & 0 deletions src/util/keccak/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* eslint-disable no-param-reassign */
const { Transform } = require('readable-stream');

module.exports = KeccakState => class Keccak extends Transform {
constructor(rate, capacity, delimitedSuffix, hashBitLength, options) {
super(options);

this._rate = rate;
this._capacity = capacity;
this._delimitedSuffix = delimitedSuffix;
this._hashBitLength = hashBitLength;
this._options = options;

this._state = new KeccakState();
this._state.initialize(rate, capacity);
this._finalized = false;
}

update(data, encoding) {
if (!Buffer.isBuffer(data) && typeof data !== 'string') {
throw new TypeError('Data must be a string or a buffer');
}
if (this._finalized) throw new Error('Digest already called');
if (!Buffer.isBuffer(data)) data = Buffer.from(data, encoding);

this._state.absorb(data);

return this;
}

digest(encoding) {
if (this._finalized) throw new Error('Digest already called');
this._finalized = true;

if (this._delimitedSuffix) { this._state.absorbLastFewBits(this._delimitedSuffix); }
let digest = this._state.squeeze(this._hashBitLength / 8);
if (encoding !== undefined) digest = digest.toString(encoding);

this._resetState();

return digest;
}

// remove result from memory
_resetState() {
this._state.initialize(this._rate, this._capacity);
return this;
}
};
26 changes: 26 additions & 0 deletions src/util/keccak/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const createKeccak = require('./api');
const KeccakState = require('./keccak');

const keccak = bits => str => {
let msg;
if (str.slice(0, 2) === '0x') {
msg = [];
for (let i = 2, l = str.length; i < l; i += 2) { msg.push(parseInt(str.slice(i, i + 2), 16)); }
msg = Buffer.from(msg);
} else {
msg = str;
}
let instance;
const Keccak = createKeccak(KeccakState);
if (bits === 256) {
instance = new Keccak(1088, 512, null, 256);
} else {
instance = new Keccak(576, 1024, null, 512);
}
return `0x${instance.update(msg).digest('hex')}`;
};

export const keccak256 = keccak(256);
export const keccak512 = keccak(512);
export const keccak256s = keccak(256);
export const keccak512s = keccak(512);
255 changes: 255 additions & 0 deletions src/util/keccak/keccak.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
/* eslint-disable no-param-reassign */
const P1600_ROUND_CONSTANTS = [
1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0,
2147483649, 0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0,
2147516425, 0, 2147483658, 0, 2147516555, 0, 139, 2147483648, 32905,
2147483648, 32771, 2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0,
2147483658, 2147483648, 2147516545, 2147483648, 32896, 2147483648, 2147483649,
0, 2147516424, 2147483648,
];

const keccakState = {
p1600(s) {
for (let round = 0; round < 24; ++round) {
// theta
const lo0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
const hi0 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
const lo1 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
const hi1 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
const lo2 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
const hi2 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
const lo3 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
const hi3 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
const lo4 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
const hi4 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];

let lo = lo4 ^ ((lo1 << 1) | (hi1 >>> 31));
let hi = hi4 ^ ((hi1 << 1) | (lo1 >>> 31));
const t1slo0 = s[0] ^ lo;
const t1shi0 = s[1] ^ hi;
const t1slo5 = s[10] ^ lo;
const t1shi5 = s[11] ^ hi;
const t1slo10 = s[20] ^ lo;
const t1shi10 = s[21] ^ hi;
const t1slo15 = s[30] ^ lo;
const t1shi15 = s[31] ^ hi;
const t1slo20 = s[40] ^ lo;
const t1shi20 = s[41] ^ hi;
lo = lo0 ^ ((lo2 << 1) | (hi2 >>> 31));
hi = hi0 ^ ((hi2 << 1) | (lo2 >>> 31));
const t1slo1 = s[2] ^ lo;
const t1shi1 = s[3] ^ hi;
const t1slo6 = s[12] ^ lo;
const t1shi6 = s[13] ^ hi;
const t1slo11 = s[22] ^ lo;
const t1shi11 = s[23] ^ hi;
const t1slo16 = s[32] ^ lo;
const t1shi16 = s[33] ^ hi;
const t1slo21 = s[42] ^ lo;
const t1shi21 = s[43] ^ hi;
lo = lo1 ^ ((lo3 << 1) | (hi3 >>> 31));
hi = hi1 ^ ((hi3 << 1) | (lo3 >>> 31));
const t1slo2 = s[4] ^ lo;
const t1shi2 = s[5] ^ hi;
const t1slo7 = s[14] ^ lo;
const t1shi7 = s[15] ^ hi;
const t1slo12 = s[24] ^ lo;
const t1shi12 = s[25] ^ hi;
const t1slo17 = s[34] ^ lo;
const t1shi17 = s[35] ^ hi;
const t1slo22 = s[44] ^ lo;
const t1shi22 = s[45] ^ hi;
lo = lo2 ^ ((lo4 << 1) | (hi4 >>> 31));
hi = hi2 ^ ((hi4 << 1) | (lo4 >>> 31));
const t1slo3 = s[6] ^ lo;
const t1shi3 = s[7] ^ hi;
const t1slo8 = s[16] ^ lo;
const t1shi8 = s[17] ^ hi;
const t1slo13 = s[26] ^ lo;
const t1shi13 = s[27] ^ hi;
const t1slo18 = s[36] ^ lo;
const t1shi18 = s[37] ^ hi;
const t1slo23 = s[46] ^ lo;
const t1shi23 = s[47] ^ hi;
lo = lo3 ^ ((lo0 << 1) | (hi0 >>> 31));
hi = hi3 ^ ((hi0 << 1) | (lo0 >>> 31));
const t1slo4 = s[8] ^ lo;
const t1shi4 = s[9] ^ hi;
const t1slo9 = s[18] ^ lo;
const t1shi9 = s[19] ^ hi;
const t1slo14 = s[28] ^ lo;
const t1shi14 = s[29] ^ hi;
const t1slo19 = s[38] ^ lo;
const t1shi19 = s[39] ^ hi;
const t1slo24 = s[48] ^ lo;
const t1shi24 = s[49] ^ hi;

// rho & pi
const t2slo0 = t1slo0;
const t2shi0 = t1shi0;
const t2slo16 = (t1shi5 << 4) | (t1slo5 >>> 28);
const t2shi16 = (t1slo5 << 4) | (t1shi5 >>> 28);
const t2slo7 = (t1slo10 << 3) | (t1shi10 >>> 29);
const t2shi7 = (t1shi10 << 3) | (t1slo10 >>> 29);
const t2slo23 = (t1shi15 << 9) | (t1slo15 >>> 23);
const t2shi23 = (t1slo15 << 9) | (t1shi15 >>> 23);
const t2slo14 = (t1slo20 << 18) | (t1shi20 >>> 14);
const t2shi14 = (t1shi20 << 18) | (t1slo20 >>> 14);
const t2slo10 = (t1slo1 << 1) | (t1shi1 >>> 31);
const t2shi10 = (t1shi1 << 1) | (t1slo1 >>> 31);
const t2slo1 = (t1shi6 << 12) | (t1slo6 >>> 20);
const t2shi1 = (t1slo6 << 12) | (t1shi6 >>> 20);
const t2slo17 = (t1slo11 << 10) | (t1shi11 >>> 22);
const t2shi17 = (t1shi11 << 10) | (t1slo11 >>> 22);
const t2slo8 = (t1shi16 << 13) | (t1slo16 >>> 19);
const t2shi8 = (t1slo16 << 13) | (t1shi16 >>> 19);
const t2slo24 = (t1slo21 << 2) | (t1shi21 >>> 30);
const t2shi24 = (t1shi21 << 2) | (t1slo21 >>> 30);
const t2slo20 = (t1shi2 << 30) | (t1slo2 >>> 2);
const t2shi20 = (t1slo2 << 30) | (t1shi2 >>> 2);
const t2slo11 = (t1slo7 << 6) | (t1shi7 >>> 26);
const t2shi11 = (t1shi7 << 6) | (t1slo7 >>> 26);
const t2slo2 = (t1shi12 << 11) | (t1slo12 >>> 21);
const t2shi2 = (t1slo12 << 11) | (t1shi12 >>> 21);
const t2slo18 = (t1slo17 << 15) | (t1shi17 >>> 17);
const t2shi18 = (t1shi17 << 15) | (t1slo17 >>> 17);
const t2slo9 = (t1shi22 << 29) | (t1slo22 >>> 3);
const t2shi9 = (t1slo22 << 29) | (t1shi22 >>> 3);
const t2slo5 = (t1slo3 << 28) | (t1shi3 >>> 4);
const t2shi5 = (t1shi3 << 28) | (t1slo3 >>> 4);
const t2slo21 = (t1shi8 << 23) | (t1slo8 >>> 9);
const t2shi21 = (t1slo8 << 23) | (t1shi8 >>> 9);
const t2slo12 = (t1slo13 << 25) | (t1shi13 >>> 7);
const t2shi12 = (t1shi13 << 25) | (t1slo13 >>> 7);
const t2slo3 = (t1slo18 << 21) | (t1shi18 >>> 11);
const t2shi3 = (t1shi18 << 21) | (t1slo18 >>> 11);
const t2slo19 = (t1shi23 << 24) | (t1slo23 >>> 8);
const t2shi19 = (t1slo23 << 24) | (t1shi23 >>> 8);
const t2slo15 = (t1slo4 << 27) | (t1shi4 >>> 5);
const t2shi15 = (t1shi4 << 27) | (t1slo4 >>> 5);
const t2slo6 = (t1slo9 << 20) | (t1shi9 >>> 12);
const t2shi6 = (t1shi9 << 20) | (t1slo9 >>> 12);
const t2slo22 = (t1shi14 << 7) | (t1slo14 >>> 25);
const t2shi22 = (t1slo14 << 7) | (t1shi14 >>> 25);
const t2slo13 = (t1slo19 << 8) | (t1shi19 >>> 24);
const t2shi13 = (t1shi19 << 8) | (t1slo19 >>> 24);
const t2slo4 = (t1slo24 << 14) | (t1shi24 >>> 18);
const t2shi4 = (t1shi24 << 14) | (t1slo24 >>> 18);

// chi
s[0] = t2slo0 ^ (~t2slo1 & t2slo2);
s[1] = t2shi0 ^ (~t2shi1 & t2shi2);
s[10] = t2slo5 ^ (~t2slo6 & t2slo7);
s[11] = t2shi5 ^ (~t2shi6 & t2shi7);
s[20] = t2slo10 ^ (~t2slo11 & t2slo12);
s[21] = t2shi10 ^ (~t2shi11 & t2shi12);
s[30] = t2slo15 ^ (~t2slo16 & t2slo17);
s[31] = t2shi15 ^ (~t2shi16 & t2shi17);
s[40] = t2slo20 ^ (~t2slo21 & t2slo22);
s[41] = t2shi20 ^ (~t2shi21 & t2shi22);
s[2] = t2slo1 ^ (~t2slo2 & t2slo3);
s[3] = t2shi1 ^ (~t2shi2 & t2shi3);
s[12] = t2slo6 ^ (~t2slo7 & t2slo8);
s[13] = t2shi6 ^ (~t2shi7 & t2shi8);
s[22] = t2slo11 ^ (~t2slo12 & t2slo13);
s[23] = t2shi11 ^ (~t2shi12 & t2shi13);
s[32] = t2slo16 ^ (~t2slo17 & t2slo18);
s[33] = t2shi16 ^ (~t2shi17 & t2shi18);
s[42] = t2slo21 ^ (~t2slo22 & t2slo23);
s[43] = t2shi21 ^ (~t2shi22 & t2shi23);
s[4] = t2slo2 ^ (~t2slo3 & t2slo4);
s[5] = t2shi2 ^ (~t2shi3 & t2shi4);
s[14] = t2slo7 ^ (~t2slo8 & t2slo9);
s[15] = t2shi7 ^ (~t2shi8 & t2shi9);
s[24] = t2slo12 ^ (~t2slo13 & t2slo14);
s[25] = t2shi12 ^ (~t2shi13 & t2shi14);
s[34] = t2slo17 ^ (~t2slo18 & t2slo19);
s[35] = t2shi17 ^ (~t2shi18 & t2shi19);
s[44] = t2slo22 ^ (~t2slo23 & t2slo24);
s[45] = t2shi22 ^ (~t2shi23 & t2shi24);
s[6] = t2slo3 ^ (~t2slo4 & t2slo0);
s[7] = t2shi3 ^ (~t2shi4 & t2shi0);
s[16] = t2slo8 ^ (~t2slo9 & t2slo5);
s[17] = t2shi8 ^ (~t2shi9 & t2shi5);
s[26] = t2slo13 ^ (~t2slo14 & t2slo10);
s[27] = t2shi13 ^ (~t2shi14 & t2shi10);
s[36] = t2slo18 ^ (~t2slo19 & t2slo15);
s[37] = t2shi18 ^ (~t2shi19 & t2shi15);
s[46] = t2slo23 ^ (~t2slo24 & t2slo20);
s[47] = t2shi23 ^ (~t2shi24 & t2shi20);
s[8] = t2slo4 ^ (~t2slo0 & t2slo1);
s[9] = t2shi4 ^ (~t2shi0 & t2shi1);
s[18] = t2slo9 ^ (~t2slo5 & t2slo6);
s[19] = t2shi9 ^ (~t2shi5 & t2shi6);
s[28] = t2slo14 ^ (~t2slo10 & t2slo11);
s[29] = t2shi14 ^ (~t2shi10 & t2shi11);
s[38] = t2slo19 ^ (~t2slo15 & t2slo16);
s[39] = t2shi19 ^ (~t2shi15 & t2shi16);
s[48] = t2slo24 ^ (~t2slo20 & t2slo21);
s[49] = t2shi24 ^ (~t2shi20 & t2shi21);

// iota
s[0] ^= P1600_ROUND_CONSTANTS[round * 2];
s[1] ^= P1600_ROUND_CONSTANTS[round * 2 + 1];
}
},
};

function Keccak() {
// much faster than `new Array(50)`
this.state = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];

this.blockSize = null;
this.count = 0;
this.squeezing = false;
}

Keccak.prototype.initialize = function (rate) {
for (let i = 0; i < 50; ++i) this.state[i] = 0;
this.blockSize = rate / 8;
this.count = 0;
this.squeezing = false;
};

Keccak.prototype.absorb = function (data) {
for (let i = 0; i < data.length; ++i) {
this.state[~~(this.count / 4)] ^= data[i] << (8 * (this.count % 4));
this.count += 1;
if (this.count === this.blockSize) {
keccakState.p1600(this.state);
this.count = 0;
}
}
};

Keccak.prototype.absorbLastFewBits = function (bits) {
this.state[~~(this.count / 4)] ^= bits << (8 * (this.count % 4));
if ((bits & 0x80) !== 0 && this.count === this.blockSize - 1) {
keccakState.p1600(this.state);
}
this.state[~~((this.blockSize - 1) / 4)]
^= 0x80 << (8 * ((this.blockSize - 1) % 4));
keccakState.p1600(this.state);
this.count = 0;
this.squeezing = true;
};

Keccak.prototype.squeeze = function (length) {
if (!this.squeezing) this.absorbLastFewBits(0x01);

const output = Buffer.alloc(length);
for (let i = 0; i < length; ++i) {
output[i] = (this.state[~~(this.count / 4)] >>> (8 * (this.count % 4))) & 0xff;
this.count += 1;
if (this.count === this.blockSize) {
keccakState.p1600(this.state);
this.count = 0;
}
}

return output;
};

module.exports = Keccak;
10 changes: 5 additions & 5 deletions src/util/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,16 +291,16 @@ export const toWei = (number, unit) => {

/**
* Takes and input transforms it into bignumber and if it is negative value, into two's complement
*
* bignumber.js get rid of round + floor in 6.0 https://github.com/MikeMcl/bignumber.js/issues/139
* the method lessThan was named isLessThan after 6.0 https://github.com/MikeMcl/bignumber.js/issues/152
* @method toTwosComplement
* @param {Number|String|BigNumber} number
* @return {BigNumber}
*/
export const toTwosComplement = number => {
const bigNumber = toBigNumber(number).round();
if (bigNumber.lessThan(0)) {
return new BigNumber(UNSIGNED_256_INT, 16)
.plus(bigNumber).plus(1);
const bigNumber = toBigNumber(number).integerValue();
if (bigNumber.isLessThan(0)) {
return new BigNumber(UNSIGNED_256_INT, 16).plus(bigNumber).plus(1);
}
return bigNumber;
};
Expand Down
1 change: 0 additions & 1 deletion test/unit/util/bloom.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ describe('test bloom',() => {
const codePointToInt =
BloomModuleRewireAPI.__GetDependency__('codePointToInt');
expect(() => codePointToInt(0)).toThrow('invalid bloom');
console.log(BloomModuleRewireAPI.__get__('codePointToInt'));
})
test('test is event in', () => {
expect(isEventInBloom(bloom, 'Burned')).toBeTruthy();
Expand Down
8 changes: 7 additions & 1 deletion test/unit/util/hash.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { keccak256, keccak512 } from '../../../src/util/hash';
import { keccak256, keccak512 } from '../../../src/util/keccak';

describe('test hash', () => {
test('test keccak256', () => {
Expand All @@ -14,6 +14,9 @@ describe('test hash', () => {
expect(keccak256('עִבְרִית')).toBe(
'0x06dc92b80132d6481c4acafbcb3b42c9146c62ae26a7a58bac94ed69c2337aa7'
);
expect(keccak256('𠜎')).toBe(
'0x16a7cc7a58444cbf7e939611910ddc82e7cba65a99d3e8e08cfcda53180a2180'
);
expect(
keccak256(
'a873e0c67ca639026b6683008f7aa6324d4979550e9bce064ca1e1fb97a30b147a24f3f666c0a72d71348ede701cf2d17e2253c34d1ec3b647dbcef2f879f4eb881c4830b791378c901eb725ea5c172316c6d606e0af7df4df7f76e490cd30b2badf45685f'
Expand All @@ -35,6 +38,9 @@ describe('test hash', () => {
expect(keccak512('עִבְרִית')).toBe(
'0x2688b4373bf42117872da97c9081a5d4c40e5c8ceb945ee880ecd6ffa10f4b0f699ef38f1414397f60e5be2f10b8eb35bc191ff0cca97785330ecb8502d4b2a9'
);
expect(keccak512('𠜎')).toBe(
'0x8a2d72022ce19d989dbe6a0017faccbf5dc2e22c162d1c5eb168864d32dd1a71e1b4782652c148cf6ca47b77a72c96fff682e72bdfef0566d4b7cca3c9ccc59d'
);
expect(
keccak512(
'a873e0c67ca639026b6683008f7aa6324d4979550e9bce064ca1e1fb97a30b147a24f3f666c0a72d71348ede701cf2d17e2253c34d1ec3b647dbcef2f879f4eb881c4830b791378c901eb725ea5c172316c6d606e0af7df4df7f76e490cd30b2badf45685f'
Expand Down
Loading

0 comments on commit af44292

Please sign in to comment.