Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(eddsa-poseidon): fixes carry handling of g function by introducing wrapper over original implementation #361

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/eddsa-poseidon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"dependencies": {
"@zk-kit/baby-jubjub": "1.0.3",
"@zk-kit/utils": "1.2.1",
"blake-hash": "^2.0.0",
"blakejs": "^1.2.1",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Does the blakejs library provide a blake1 hash which could be used here instead?
  • Alternately, have you checked whether the blakejs library might have the same error that this PR is fixing, and need its own update for the blake2b use case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the blakejs library provide a blake1 hash which could be used here instead?

blakejs should only provide the following implementations: BLAKE2b and BLAKE2s.

Alternately, have you checked whether the blakejs library might have the same error that this PR is fixing, and need its own update for the blake2b use case?

Let me tag @thogiti for this question.

"buffer": "6.0.3",
"poseidon-lite": "0.3.0"
Expand Down
289 changes: 17 additions & 272 deletions packages/eddsa-poseidon/src/blake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,277 +14,39 @@
* hashes of input data, providing both one-time hashing capabilities and incremental
* hashing to process large or streaming data.
*
* This code is adapted from the "blake-hash" JavaScript library, ensuring compatibility
* and performance in TypeScript environments. It supports hashing with optional
* salt for enhanced security in certain contexts.
* This code is adapted to serve as a wrapper around the "blake-hash" JavaScript library,
* ensuring compatibility and performance in TypeScript environments. It supports hashing
* with incremental updates and final digest retrieval.
*/

import { Buffer } from "buffer"
import createBlakeHash from "blake-hash"
import { HashFunction } from "./HashFunction"

const zo = Buffer.from([0x01])
const oo = Buffer.from([0x81])

// Static properties for sigma, u256, u512, and padding are defined here below
const sigma = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]
]

const u512 = [
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6,
0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947,
0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69
]

const padding = Buffer.from([
0x80, 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, 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, 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, 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, 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, 0, 0, 0
])

/**
* Performs a bitwise rotation on the values of two elements in an array.
* This operation is a key component of the Blake-512 algorithm, enabling
* the mixing of bits in a non-linear fashion.
* @param v The array containing values to rotate.
* @param i The index of the first element to rotate.
* @param j The index of the second element to rotate.
* @param n The number of bits to rotate by.
*/

function rot(v: number[], i: number, j: number, n: number): void {
let hi = v[i * 2] ^ v[j * 2]
let lo = v[i * 2 + 1] ^ v[j * 2 + 1]

if (n >= 32) {
lo ^= hi
hi ^= lo
lo ^= hi
n -= 32
}

if (n === 0) {
v[i * 2] = hi >>> 0
v[i * 2 + 1] = lo >>> 0
} else {
v[i * 2] = ((hi >>> n) | (lo << (32 - n))) >>> 0
v[i * 2 + 1] = ((lo >>> n) | (hi << (32 - n))) >>> 0
}
}

/**
* The G function is one of the core operations in the Blake-512 compression function.
* It mixes the input values based on the message block and the round constants,
* contributing to the diffusion and confusion properties of the hash function.
* @param v The working vector, part of the state being updated.
* @param m - The message block being processed.
* @param i The current round index.
* @param a, b, c, d Indices within the working vector to mix.
* @param e Index within the message block and round constants.
*/
function g(v: number[], m: number[], i: number, a: number, b: number, c: number, d: number, e: number): void {
let lo

// v[a] += (m[sigma[i][e]] ^ u512[sigma[i][e+1]]) + v[b];
lo = v[a * 2 + 1] + ((m[sigma[i][e] * 2 + 1] ^ u512[sigma[i][e + 1] * 2 + 1]) >>> 0) + v[b * 2 + 1]
v[a * 2] =
(v[a * 2] + ((m[sigma[i][e] * 2] ^ u512[sigma[i][e + 1] * 2]) >>> 0) + v[b * 2] + ~~(lo / 0x0100000000)) >>> 0
v[a * 2 + 1] = lo >>> 0

// v[d] = ROT( v[d] ^ v[a],32);
rot(v, d, a, 32)

// v[c] += v[d];
lo = v[c * 2 + 1] + v[d * 2 + 1]
v[c * 2] = (v[c * 2] + v[d * 2] + ~~(lo / 0x0100000000)) >>> 0
v[c * 2 + 1] = lo >>> 0

// v[b] = ROT( v[b] ^ v[c],25);
rot(v, b, c, 25)

// v[a] += (m[sigma[i][e+1]] ^ u512[sigma[i][e]])+v[b];
lo = v[a * 2 + 1] + ((m[sigma[i][e + 1] * 2 + 1] ^ u512[sigma[i][e] * 2 + 1]) >>> 0) + v[b * 2 + 1]
v[a * 2] =
(v[a * 2] + ((m[sigma[i][e + 1] * 2] ^ u512[sigma[i][e] * 2]) >>> 0) + v[b * 2] + ~~(lo / 0x0100000000)) >>> 0
v[a * 2 + 1] = lo >>> 0

// v[d] = ROT( v[d] ^ v[a],16);
rot(v, d, a, 16)

// v[c] += v[d];
lo = v[c * 2 + 1] + v[d * 2 + 1]
v[c * 2] = (v[c * 2] + v[d * 2] + ~~(lo / 0x0100000000)) >>> 0
v[c * 2 + 1] = lo >>> 0

// v[b] = ROT( v[b] ^ v[c],11)
rot(v, b, c, 11)
}

/**
* Processes the carry for the bit length counter, ensuring it remains
* within bounds as a 128-bit number.
* @param arr The array representing the 128-bit counter.
*/
function lengthCarry(arr: number[]) {
for (let j = 0; j < arr.length; j += 1) {
if (arr[j] < 0x0100000000) break
arr[j] -= 0x0100000000
arr[j + 1] += 1
}
}

/**
* Represents a Blake-512 hash computation instance.
* This class maintains the internal state, buffers, and counters needed to
* process input data and produce the final hash output. It supports incremental
* hashing, allowing data to be added in chunks.
* Represents a Blake-512 hash computation instance, wrapping the 'blake-hash' library.
* This class maintains an internal instance of the underlying library to process input data.
* It supports incremental hashing, allowing data to be added in chunks.
*/
/* eslint-disable import/prefer-default-export */
export default class Blake512 implements HashFunction {
private _h: number[]
private _s: number[]
private _block: Buffer
private _blockOffset: number
private _length: number[]
private _zo: Buffer
private _oo: Buffer
private _nullt: boolean
private _hash: ReturnType<typeof createBlakeHash>

/**
* Initializes a new Blake-512 hash instance with the default parameters.
* Initializes a new Blake-512 hash instance by creating an instance
* of the underlying blake-hash library for the "blake512" variant.
*/
constructor() {
this._h = [
0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1, 0x510e527f,
0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179
]

this._s = [0, 0, 0, 0, 0, 0, 0, 0]

this._block = Buffer.alloc(128)
this._blockOffset = 0
this._length = [0, 0, 0, 0]

this._nullt = false

this._zo = zo
this._oo = oo
}

/**
* The core compression function for Blake-512. It transforms the internal
* state based on the input block and the current hash parameters.
*/
_compress() {
const v = new Array(32)
const m = new Array(32)
let i

for (i = 0; i < 32; i += 1) m[i] = this._block.readUInt32BE(i * 4)
for (i = 0; i < 16; i += 1) v[i] = this._h[i] >>> 0
for (i = 16; i < 24; i += 1) v[i] = (this._s[i - 16] ^ u512[i - 16]) >>> 0
for (i = 24; i < 32; i += 1) v[i] = u512[i - 16]

if (!this._nullt) {
v[24] = (v[24] ^ this._length[1]) >>> 0
v[25] = (v[25] ^ this._length[0]) >>> 0
v[26] = (v[26] ^ this._length[1]) >>> 0
v[27] = (v[27] ^ this._length[0]) >>> 0
v[28] = (v[28] ^ this._length[3]) >>> 0
v[29] = (v[29] ^ this._length[2]) >>> 0
v[30] = (v[30] ^ this._length[3]) >>> 0
v[31] = (v[31] ^ this._length[2]) >>> 0
}

for (i = 0; i < 16; i += 1) {
/* column step */
g(v, m, i, 0, 4, 8, 12, 0)
g(v, m, i, 1, 5, 9, 13, 2)
g(v, m, i, 2, 6, 10, 14, 4)
g(v, m, i, 3, 7, 11, 15, 6)
/* diagonal step */
g(v, m, i, 0, 5, 10, 15, 8)
g(v, m, i, 1, 6, 11, 12, 10)
g(v, m, i, 2, 7, 8, 13, 12)
g(v, m, i, 3, 4, 9, 14, 14)
}

for (i = 0; i < 16; i += 1) {
this._h[(i % 8) * 2] = (this._h[(i % 8) * 2] ^ v[i * 2]) >>> 0
this._h[(i % 8) * 2 + 1] = (this._h[(i % 8) * 2 + 1] ^ v[i * 2 + 1]) >>> 0
}

for (i = 0; i < 8; i += 1) {
this._h[i * 2] = (this._h[i * 2] ^ this._s[(i % 4) * 2]) >>> 0
this._h[i * 2 + 1] = (this._h[i * 2 + 1] ^ this._s[(i % 4) * 2 + 1]) >>> 0
}
}

/**
* Adds padding to the message as per the Blake-512 specification, ensuring
* the message length is a multiple of the block size.
*/
_padding() {
const len = this._length.slice()
len[0] += this._blockOffset * 8
lengthCarry(len)

const msglen = Buffer.alloc(16)
for (let i = 0; i < 4; i += 1) msglen.writeUInt32BE(len[3 - i], i * 4)

if (this._blockOffset === 111) {
this._length[0] -= 8
this.update(this._oo)
} else {
if (this._blockOffset < 111) {
if (this._blockOffset === 0) this._nullt = true
this._length[0] -= (111 - this._blockOffset) * 8
this.update(padding.subarray(0, 111 - this._blockOffset))
} else {
this._length[0] -= (128 - this._blockOffset) * 8
this.update(padding.subarray(0, 128 - this._blockOffset))
this._length[0] -= 111 * 8
this.update(padding.subarray(1, 1 + 111))
this._nullt = true
}

this.update(this._zo)
this._length[0] -= 8
}

this._length[0] -= 128
this.update(msglen)
this._hash = createBlakeHash("blake512")
}

/**
* Completes the hash computation and returns the final hash value.
* This method applies the necessary padding, performs the final compression,
* and returns the hash output.
* @returns The Blake-512 hash of the input data.
* @returns The Blake-512 hash of the input data as a Buffer.
*/
digest(): Buffer {
this._padding()

const buffer = Buffer.alloc(64)
for (let i = 0; i < 16; i += 1) buffer.writeUInt32BE(this._h[i], i * 4)
return buffer
// The underlying blake-hash returns a Buffer by default.
return this._hash.digest() as Buffer
}

/**
Expand All @@ -293,26 +55,9 @@ export default class Blake512 implements HashFunction {
* @param data The data to add to the hash.
* @returns This instance, to allow method chaining.
*/
update(data: Buffer) {
const block = this._block
let offset = 0

while (this._blockOffset + data.length - offset >= block.length) {
for (let i = this._blockOffset; i < block.length; )
/* eslint-disable no-plusplus */
block[i++] = data[offset++]

this._length[0] += block.length * 8
lengthCarry(this._length)

this._compress()
this._blockOffset = 0
}

while (offset < data.length)
/* eslint-disable no-plusplus */
block[this._blockOffset++] = data[offset++]

update(data: Buffer | Uint8Array): this {
// The underlying library expects a Buffer or Uint8Array, which we already have.
this._hash.update(data)
return this
}
}
15 changes: 15 additions & 0 deletions packages/eddsa-poseidon/src/types/blake-hash.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
declare module "blake-hash" {
type BlakeVariant = "blake224" | "blake256" | "blake384" | "blake512"

export interface BlakeHash {
update(data: Buffer | Uint8Array): BlakeHash
digest(encoding?: "hex" | "binary"): Buffer | string
}

/**
* Creates an instance of a BLAKE hash function.
* @param variant The desired BLAKE variant ('blake224', 'blake256', 'blake384', or 'blake512').
* @returns An instance of the hash function.
*/
export default function createBlakeHash(variant: BlakeVariant): BlakeHash
}
15 changes: 15 additions & 0 deletions packages/poseidon-cipher/src/types/blake-hash.d.ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this type in the poseidon-chipher lib?

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
declare module "blake-hash" {
type BlakeVariant = "blake224" | "blake256" | "blake384" | "blake512"

export interface BlakeHash {
update(data: Buffer | Uint8Array): BlakeHash
digest(encoding?: "hex" | "binary"): Buffer | string
}

/**
* Creates an instance of a BLAKE hash function.
* @param variant The desired BLAKE variant ('blake224', 'blake256', 'blake384', or 'blake512').
* @returns An instance of the hash function.
*/
export default function createBlakeHash(variant: BlakeVariant): BlakeHash
}
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3092,6 +3092,7 @@ __metadata:
"@rollup/plugin-typescript": "npm:^11.1.6"
"@zk-kit/baby-jubjub": "npm:1.0.3"
"@zk-kit/utils": "npm:1.2.1"
blake-hash: "npm:^2.0.0"
blakejs: "npm:^1.2.1"
buffer: "npm:6.0.3"
circomlibjs: "npm:0.0.8"
Expand Down
Loading