Skip to content

Commit

Permalink
Merge pull request #10 from iden3/feature/add-sponge-poseidon
Browse files Browse the repository at this point in the history
Add sponge hash to poseidon
  • Loading branch information
Kolezhniuk authored Mar 17, 2023
2 parents 3665c4f + 708dc30 commit f845d1a
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 12 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@iden3/js-crypto",
"version": "1.0.0-beta.0",
"version": "1.0.0-beta.1",
"description": "",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
58 changes: 51 additions & 7 deletions src/poseidon/poseidon-opt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,27 @@ export class Poseidon {
return F.normalize(state[0]);
}

// HashBytes returns a sponge hash of a msg byte slice split into blocks of 31 bytes
static hashBytes(msg: Uint8Array): bigint {
const inputs = new Array(SPONGE_INPUTS).fill(BigInt(0));
return Poseidon.hashBytesX(msg, SPONGE_INPUTS);
}

// hashBytesX returns a sponge hash of a msg byte slice split into blocks of 31 bytes
static hashBytesX(msg: Uint8Array, frameSize: number): bigint {
const inputs = new Array(frameSize).fill(BigInt(0));
let dirty = false;
let hash: bigint;
let hash!: bigint;

let k = 0;
for (let i = 0; i < parseInt(`${msg.length / SPONGE_CHUNK_SIZE}`); i += 1) {
dirty = true;
inputs[k] = utils.beBuff2int(msg.slice(SPONGE_CHUNK_SIZE * i, SPONGE_CHUNK_SIZE * (i + 1)));
if (k === SPONGE_INPUTS - 1) {
if (k === frameSize - 1) {
hash = Poseidon.hash(inputs);
dirty = false;
inputs[0] = hash.valueOf();
inputs[0] = hash;
inputs.fill(BigInt(0), 1, SPONGE_CHUNK_SIZE);
for (let j = 1; j < SPONGE_INPUTS; j += 1) {
for (let j = 1; j < frameSize; j += 1) {
inputs[j] = BigInt(0);
}
k = 1;
Expand All @@ -106,8 +112,46 @@ export class Poseidon {
hash = Poseidon.hash(inputs);
}

// @ts-ignore: if we reach here then hash should be assigned value
return hash.valueOf();
return hash;
}

// SpongeHashX returns a sponge hash of inputs using Poseidon with configurable frame size
static spongeHashX(inputs: bigint[], frameSize: number): bigint {
if (frameSize < 2 || frameSize > 16) {
throw new Error('incorrect frame size');
}

// not used frame default to zero
let frame = new Array(frameSize).fill(BigInt(0));

let dirty = false;
let hash!: bigint;

let k = 0;
for (let i = 0; i < inputs.length; i++) {
dirty = true;
frame[k] = inputs[i];
if (k === frameSize - 1) {
hash = this.hash(frame);
dirty = false;
frame = new Array(frameSize).fill(BigInt(0));
frame[0] = hash;
k = 1;
} else {
k++;
}
}

if (dirty) {
// we haven't hashed something in the main sponge loop and need to do hash here
hash = this.hash(frame);
}

if (!hash) {
throw new Error('hash is undefined');
}

return hash;
}
}
export const poseidon = Poseidon;
59 changes: 57 additions & 2 deletions tests/poseidon.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@
import { poseidon } from '../src';
import { testVectors } from './test-vectors';
import { Hex, poseidon } from '../src';
import ByteBuffer from 'bytebuffer';

describe('Poseidon test', () => {
function fromLittleEndian(bytes: Uint8Array): bigint {
const n256 = BigInt(256);
let result = BigInt(0);
let base = BigInt(1);
bytes.forEach((byte) => {
result += base * BigInt(byte);
base = base * n256;
});
return result;
}

function fromBigEndian(bytes: Uint8Array): bigint {
return fromLittleEndian(bytes.reverse());
}

function splitBytes(b: Uint8Array, chunkSize: number): Uint8Array[] {
if (!b.length) {
return [];
}
if (chunkSize >= b.length) {
return [b];
}
const chunks: Uint8Array[] = [];
let currentLen = 0;
let currentStart = 0;
for (let i = 0; i < b.length; i++) {
if (currentLen == chunkSize) {
const chunk = b.slice(currentStart, i);
chunks.push(chunk);
currentLen = 0;
currentStart = i;
}
currentLen++;
}
chunks.push(b.slice(currentStart, b.length));
return chunks;
}

describe('Poseidon test', () => {
it('test 2 inputs', () => {
const inputs = [1, 2].map((v) => BigInt(v));
const res = poseidon.hash(inputs);
Expand Down Expand Up @@ -75,4 +113,21 @@ describe('Poseidon test', () => {
expect(poseidon.hashBytes(new Uint8Array(input)).toString(16)).toEqual(expectedHash);
});
});

it('TestSpongeHashX', () => {
for (let i = 0; i < testVectors.length; i++) {
const vector = testVectors[i];
const b = Hex.decodeString(vector.hexString);
const chunks = splitBytes(b, 31);

const inputs: bigint[] = [];

for (let i = 0; i < chunks.length; i++) {
inputs[i] = fromBigEndian(chunks[i]);
}

const res = poseidon.spongeHashX(inputs, vector.frameSize);
expect(res.toString(16)).toEqual(vector.expectedHash);
}
});
});
Loading

0 comments on commit f845d1a

Please sign in to comment.