Skip to content

Commit

Permalink
Merge pull request #115 from seshanthS/feat/MGF1
Browse files Browse the repository at this point in the history
feat: Implement MGF1
  • Loading branch information
0xturboblitz authored Jun 4, 2024
2 parents 57ec1c3 + 106015a commit 6d7bc6e
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
pragma circom 2.1.5;

include "../../utils/Mgf1Sha256.circom";
include "../../../node_modules/circomlib/circuits/bitify.circom";


template Mgf1Sha256_1ByteMask_tester(seed_len_bytes, mask_len_bytes) {

signal input seed;
signal input expected_mask_output[mask_len_bytes * 8];
signal output mask[mask_len_bytes * 8];

component mgf1_sha256 = Mgf1Sha256(seed_len_bytes, mask_len_bytes);
component num2Bits = Num2Bits(seed_len_bytes * 8);
num2Bits.in <== seed;

for (var i=0; i < seed_len_bytes * 8; i++) {
mgf1_sha256.seed[i] <== num2Bits.out[i];
}

for (var i=0; i < mask_len_bytes * 8; i++) {
mask[i] <== mgf1_sha256.out[i];
}

for (var i=0; i < mask_len_bytes * 8; i++) {
mgf1_sha256.out[i] === expected_mask_output[i];
}

}

component main = Mgf1Sha256_1ByteMask_tester(4, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
pragma circom 2.1.5;

include "../../utils/Mgf1Sha256.circom";
include "../../../node_modules/circomlib/circuits/bitify.circom";


template Mgf1Sha256_32Bytes_tester(seed_len_bytes, mask_len_bytes) {

signal input seed[seed_len_bytes * 8];
signal input expected_mask_output[mask_len_bytes * 8];
signal output mask[mask_len_bytes * 8];

component mgf1_sha256 = Mgf1Sha256(seed_len_bytes, mask_len_bytes);

for (var i=0; i < seed_len_bytes * 8; i++) {
mgf1_sha256.seed[i] <== seed[i];
}

for (var i=0; i < mask_len_bytes * 8; i++) {
mask[i] <== mgf1_sha256.out[i];
}

for (var i=0; i < mask_len_bytes * 8; i++) {
mgf1_sha256.out[i] === expected_mask_output[i];
}

}

component main = Mgf1Sha256_32Bytes_tester(32, 32);
33 changes: 33 additions & 0 deletions circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
pragma circom 2.1.5;

include "../../utils/Mgf1Sha256.circom";
include "../../../node_modules/circomlib/circuits/bitify.circom";


template Mgf1Sha256_tester(seed_len_bytes, mask_len_bytes) {

signal input seed;
signal input expected_mask_output[mask_len_bytes * 8];
signal output mask[mask_len_bytes * 8];

component mgf1_sha256 = Mgf1Sha256(seed_len_bytes, mask_len_bytes);
component num2Bits = Num2Bits(seed_len_bytes * 8);
num2Bits.in <== seed;

for (var i=0; i < seed_len_bytes * 8; i++) {
mgf1_sha256.seed[i] <== num2Bits.out[i];
}

for (var i=0; i < mask_len_bytes * 8; i++) {
mask[i] <== mgf1_sha256.out[i];
}

for (var i=0; i < mask_len_bytes * 8; i++) {
mgf1_sha256.out[i] === expected_mask_output[i];
}

}

// component main { public [ seed ] } = Mgf1_sha256(4,32);

component main = Mgf1Sha256_tester(4, 32);
50 changes: 50 additions & 0 deletions circuits/circuits/utils/Mgf1Sha256.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
pragma circom 2.1.5;
include "circomlib/circuits/sha256/sha256.circom";
include "circomlib/circuits/bitify.circom";

template Mgf1Sha256(seedLen, maskLen) { //in bytes
var seedLenBits = seedLen * 8;
var maskLenBits = maskLen * 8;
var hashLen = 32; //output len of sha function in bytes
var hashLenBits = hashLen * 8;//output len of sha function in bits

signal input seed[seedLenBits]; //each represents a bit
signal output out[maskLenBits];

assert(maskLen <= 0xffffffff * hashLen );
var iterations = (maskLen \ hashLen) + 1; //adding 1, in-case maskLen \ hashLen is 0

component sha256[iterations];
component num2Bits[iterations];

for (var i = 0; i < iterations; i++) {
sha256[i] = Sha256(seedLenBits + 32); //32 bits for counter
num2Bits[i] = Num2Bits(32);
}

var concated[seedLenBits + 32]; //seed + 32 bits(4 Bytes) for counter
signal hashed[hashLenBits * (iterations)];

for (var i = 0; i < seedLenBits; i++) {
concated[i] = seed[i];
}

for (var i = 0; i < iterations; i++) {
num2Bits[i].in <== i; //convert counter to bits

for (var j = 0; j < 32; j++) {
//concat seed and counter
concated[seedLenBits + j] = num2Bits[i].out[j];
}

sha256[i].in <== concated;

for (var j = 0; j < hashLenBits; j++) {
hashed[i * hashLenBits + j] <== sha256[i].out[j];
}
}

for (var i = 0; i < maskLenBits; i++) {
out[i] <== hashed[i];
}
}
154 changes: 154 additions & 0 deletions circuits/test/Mgf1_sha256.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import path from 'path';
import { createHash, randomBytes } from 'node:crypto';
const wasm_tester = require("circom_tester").wasm;

describe('Mgf1_sha256 Circuit Test', function () {
this.timeout(0); // Disable timeout
const hashLen = 32; // SHA256 length

const compileCircuit = async (circuitPath: string) => {
return await wasm_tester(
path.join(circuitPath),
{
include: [
"node_modules",
]
}
);
}

function buffer2bitArray(b) {
const res = [];
for (let i=0; i<b.length; i++) {
for (let j=0; j<8; j++) {
res.push((b[i] >> (7-j) &1));
}
}
return res;
}

function num2Bits(n, input) {
let out = [];
for (let i = 0; i < n; i++) {
out[i] = (input >> i) & 1;
}
return out;
}

const bitArray2buffer = (a) => {
const len = Math.floor((a.length -1 )/8)+1;
const b = Buffer.alloc(len);

for (let i=0; i<a.length; i++) {
const p = Math.floor(i/8);
b[p] = b[p] | (Number(a[i]) << ( 7 - (i%8) ));
}
return b;
}

const MGF1 = (mgfSeed: Buffer, maskLen: number) => {
const hLen = hashLen;
if (maskLen > 0xffffffff * hLen) {
throw new Error('mask too long');
}

var T = [];
for (var i = 0; i <= Math.ceil(maskLen / hLen) - 1; i++) {
var C = Buffer.alloc(4);
C.writeUInt32BE(i);
const hash3 = createHash('sha256');
hash3.update(Buffer.concat([mgfSeed, C]));
T.push(hash3.digest());
}
return Buffer.concat(T).slice(0, maskLen);
}

it('Should compile', async function () {
await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom');
})

it('Should generate correct MGF1 output - 4 Byte Seed', async function () {
const seed = 12345678;
const maskLen = 32;
const seedLen = 4; // 4 bytes - set in the circuit

const bitArray = num2Bits(seedLen * 8, seed);
const mgfSeed = bitArray2buffer(bitArray);

const expected = MGF1(mgfSeed, maskLen);

const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: seed,
expected_mask_output,

};

const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});

it('Should generate correct MGF1 output - 32 Byte Seed', async function () {
const randBytes = randomBytes(32);

const maskLen = 32;
const seedLen = 32; // set in circuit
const expected = MGF1(randBytes, maskLen);

const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_32Bytes_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: buffer2bitArray(randBytes),
expected_mask_output,

};

const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});

it('Should generate correct MGF1 output - seedLen value > than actual seed length', async function () {
const seed = 1234;
const maskLen = 32;
const seedLen = 4; //set in circuit

const bitArray = num2Bits(seedLen * 8, seed);
const mgfSeed = bitArray2buffer(bitArray);

const expected = MGF1(mgfSeed, maskLen);

const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: seed,
expected_mask_output,

};

const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});

it('Should generate correct MGF1 output - maskLen == 1', async function () {
const seed = 12345678;
const maskLen = 1;
const seedLen = 4;

const bitArray = num2Bits(seedLen * 8, seed);
const mgfSeed = bitArray2buffer(bitArray);

const expected = MGF1(mgfSeed, maskLen);

const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_1ByteMask_tester.circom');
const expected_mask_output = buffer2bitArray(expected);
const inputs = {
seed: seed,
expected_mask_output,

};

const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
});
});

0 comments on commit 6d7bc6e

Please sign in to comment.