o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
301 lines (267 loc) • 8.52 kB
text/typescript
// In-circuit tests of the SHA2 family of hash functions together with
// test vectors extracted from https://www.di-mgt.com.au/sha_testvectors.html
import { ZkProgram } from '../../proof-system/zkprogram.js';
import { Bytes } from '../wrapped-classes.js';
import { Gadgets } from '../gadgets/gadgets.js';
import { sha256 as nobleSha256, sha224 as nobleSha224 } from '@noble/hashes/sha256';
import { sha384 as nobleSha384, sha512 as nobleSha512 } from '@noble/hashes/sha512';
import { bytes } from './test-utils.js';
import { equivalentAsync, equivalentProvable } from '../../testing/equivalent.js';
import { Random, sample } from '../../testing/random.js';
import { expect } from 'expect';
// SHA2-224 TESTS
{
sample(Random.nat(400), 5).forEach((preimageLength) => {
let inputBytes = bytes(preimageLength);
let outputBytes = bytes(224 / 8);
equivalentProvable({ from: [inputBytes], to: outputBytes, verbose: true })(
(x) => nobleSha224(x),
(x) => Gadgets.SHA2.hash(224, x),
`sha224 preimage length ${preimageLength}`
);
});
const Sha2_224Program = ZkProgram({
name: `sha2-224`,
publicOutput: Bytes(28),
methods: {
sha224: {
privateInputs: [Bytes(192)],
async method(preImage: Bytes) {
return {
publicOutput: Gadgets.SHA2.hash(224, preImage),
};
},
},
},
});
const RUNS = 2;
await Sha2_224Program.compile();
await equivalentAsync(
{
from: [bytes(192)],
to: bytes(28),
},
{ runs: RUNS }
)(nobleSha224, async (x) => {
const { proof } = await Sha2_224Program.sha224(x);
await Sha2_224Program.verify(proof);
return proof.publicOutput;
});
for (let { preimage, hash } of testVectors()) {
let actual = Gadgets.SHA2.hash(224, Bytes.fromString(preimage));
expect(actual.toHex()).toEqual(hash);
}
function testVectors() {
return [
{
preimage: 'abc',
hash: '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7',
},
{
preimage: 'a'.repeat(1000000),
hash: '20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67',
},
{
preimage: '',
hash: 'd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f',
},
{
preimage:
'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu',
hash: 'c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3',
},
];
}
}
// SHA2-256 TESTS
{
sample(Random.nat(400), 5).forEach((preimageLength) => {
let inputBytes = bytes(preimageLength);
let outputBytes = bytes(256 / 8);
equivalentProvable({ from: [inputBytes], to: outputBytes, verbose: true })(
(x) => nobleSha256(x),
(x) => Gadgets.SHA2.hash(256, x),
`sha256 preimage length ${preimageLength}`
);
});
const Sha2_256Program = ZkProgram({
name: `sha2-256`,
publicOutput: Bytes(32),
methods: {
sha256: {
privateInputs: [Bytes(192)],
async method(preImage: Bytes) {
return {
publicOutput: Gadgets.SHA2.hash(256, preImage),
};
},
},
},
});
const RUNS = 2;
await Sha2_256Program.compile();
await equivalentAsync(
{
from: [bytes(192)],
to: bytes(32),
},
{ runs: RUNS }
)(nobleSha256, async (x) => {
const { proof } = await Sha2_256Program.sha256(x);
await Sha2_256Program.verify(proof);
return proof.publicOutput;
});
for (let { preimage, hash } of testVectors()) {
let actual = Gadgets.SHA2.hash(256, Bytes.fromString(preimage));
expect(actual.toHex()).toEqual(hash);
}
function testVectors() {
return [
{
preimage: 'abc',
hash: 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad',
},
{
preimage: 'a'.repeat(1000000),
hash: 'cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0',
},
{
preimage: '',
hash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
},
{
preimage:
'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu',
hash: 'cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1',
},
];
}
}
// SHA2-384 TESTS
{
sample(Random.nat(400), 5).forEach((preimageLength) => {
let inputBytes = bytes(preimageLength);
let outputBytes = bytes(384 / 8);
equivalentProvable({ from: [inputBytes], to: outputBytes, verbose: true })(
(x) => nobleSha384(x),
(x) => Gadgets.SHA2.hash(384, x),
`sha384 preimage length ${preimageLength}`
);
});
const Sha2_384Program = ZkProgram({
name: `sha2-384`,
publicOutput: Bytes(48),
methods: {
sha384: {
privateInputs: [Bytes(192)],
async method(preImage: Bytes) {
return {
publicOutput: Gadgets.SHA2.hash(384, preImage),
};
},
},
},
});
const RUNS = 2;
await Sha2_384Program.compile();
await equivalentAsync(
{
from: [bytes(192)],
to: bytes(48),
},
{ runs: RUNS }
)(nobleSha384, async (x) => {
const { proof } = await Sha2_384Program.sha384(x);
await Sha2_384Program.verify(proof);
return proof.publicOutput;
});
for (let { preimage, hash } of testVectors()) {
let actual = Gadgets.SHA2.hash(384, Bytes.fromString(preimage));
expect(actual.toHex()).toEqual(hash);
}
function testVectors() {
return [
{
preimage: 'abc',
hash: 'cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7',
},
{
preimage: 'a'.repeat(1000000),
hash: '9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985',
},
{
preimage: '',
hash: '38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b',
},
{
preimage:
'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu',
hash: '09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039',
},
];
}
}
// SHA2-512 TESTS
{
sample(Random.nat(400), 5).forEach((preimageLength) => {
let inputBytes = bytes(preimageLength);
let outputBytes = bytes(512 / 8);
equivalentProvable({ from: [inputBytes], to: outputBytes, verbose: true })(
(x) => nobleSha512(x),
(x) => Gadgets.SHA2.hash(512, x),
`sha512 preimage length ${preimageLength}`
);
});
const Sha2_512Program = ZkProgram({
name: `sha2-512`,
publicOutput: Bytes(64),
methods: {
sha512: {
privateInputs: [Bytes(192)],
async method(preImage: Bytes) {
return {
publicOutput: Gadgets.SHA2.hash(512, preImage),
};
},
},
},
});
const RUNS = 2;
await Sha2_512Program.compile();
await equivalentAsync(
{
from: [bytes(192)],
to: bytes(64),
},
{ runs: RUNS }
)(nobleSha512, async (x) => {
const { proof } = await Sha2_512Program.sha512(x);
await Sha2_512Program.verify(proof);
return proof.publicOutput;
});
for (let { preimage, hash } of testVectors()) {
let actual = Gadgets.SHA2.hash(512, Bytes.fromString(preimage));
expect(actual.toHex()).toEqual(hash);
}
function testVectors() {
return [
{
preimage: 'abc',
hash: 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f',
},
{
preimage: 'a'.repeat(1000000),
hash: 'e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b',
},
{
preimage: '',
hash: 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
},
{
preimage:
'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu',
hash: '8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909',
},
];
}
}