o1-labs / o1js

TypeScript framework for zk-SNARKs and zkApps
https://docs.minaprotocol.com/en/zkapps/how-to-write-a-zkapp
Apache License 2.0
473 stars 105 forks source link

Add partial SHA256 hash gadget #1668

Closed Shigoto-dev19 closed 1 week ago

Shigoto-dev19 commented 4 weeks ago

Description

Changes

Notes on partialHash

// Read email and generate emailVerifierInputs const emailBytes = fs.readFileSync('./src/example.eml'); const inputs = await generateEmailVerifierInputs(emailBytes, { shaPrecomputeSelector: 'thousands', });

// ---- string selector method ----

// parse the precomputedSHA to be o1js compatible let chunks: UInt32[] = []; const precomputedHash = Bytes.from( inputs.precomputedSHA!.map((x) => parseInt(x)) ).bytes; for (let i = 0; i < precomputedHash.length; i += 4) { // chunk 4 bytes into one UInt32, as expected by SHA256 // bytesToWord expects little endian, so we reverse the bytes chunks.push( UInt32.Unsafe.fromField( bytesToWord(precomputedHash.slice(i, i + 4).reverse()) ) ); }

const inputSchedule = Bytes.from(inputs.emailBody!.map((x) => parseInt(x))); const inputBlocks = Bytes.from( inputSchedule.bytes.slice(0, Number(inputs.emailBodyLength)) );

const inputBits = parse512BitBlock( inputBlocks.bytes.map((x) => x.value.toBits(8).reverse()).flat() ); const messageSchedule = prepareMessageSchedule(inputBits); let out = sha256Compression(chunks, messageSchedule, K);

let partialDigest = Bytes.from( out.map((x) => wordToBytes(x.value, 4).reverse()).flat() ); console.log('computed digest; ', partialDigest.toHex());



- As seen in the code snippet above, it's possible to use `partialSHA256` in a way that is compatible with the existing `@zk-email/helpers` package, but it requires exposing more low-level SHA256 functions and a deep understanding of SHA2 mechanics to maintain the correct order of block hashing.
  - Some low-level functions that should be exposed include `bytesToWord`, `wordToBytes`, and `parseMessageBlock`.
  - In the current implementation, `@zk-email/helpers` handles outputting `precomputedHash`, `paddedInput`, and `paddedInputLength`. These are managed with helpers for the zkEmail circuit in Circom, but the good news is that all helpers for SHA256 are exposed and can be adapted for o1js partial SHA.
  - I am not sure if it's fine to expose non-provable helpers in o1js, including what's required above.
  - Perhaps publishing a side package for such helpers for o1js, giving acknowledgment to the zkEmail organization, is a good solution!

## Notes on `Dynamic SHA` 

- Having an operation `partialHash` makes it easy to implement an `Arbitrary Length SHA256 Hash Function`.
- The only difference is: 
  - Set a max size for the preimage bytes length, e.g., 1024 bytes.
  - Slice the preimage bytes given a `stringSelector`.
  - [sha256Pad](https://github.com/zkemail/zk-email-verify/blob/main/packages/helpers/src/sha-utils.ts#L97-#L123) the remaining message blocks.
  - Generate circuit inputs as in the case for [body hashing for zkEmail](https://github.com/zkemail/zk-email-verify/blob/main/packages/helpers/src/input-generators.ts#L89-#L94).
- It would be great having helpers that handle these intricacies for better devX.
Trivo25 commented 2 weeks ago

Hey @Shigoto-dev19 - I accidentally marked this PR as ready for review :D Do you need more time or can I take a look?

Shigoto-dev19 commented 2 weeks ago

Hey @Shigoto-dev19 - I accidentally marked this PR as ready for review :D Do you need more time or can I take a look?

Thank you @Trivo25 - Yes, it's functional, please take a look :))

Shigoto-dev19 commented 1 week ago

Closed upon pushing #1689