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

passing test for gfmulx #48

Merged
merged 14 commits into from
Aug 16, 2024
Merged

passing test for gfmulx #48

merged 14 commits into from
Aug 16, 2024

Conversation

0xJepsen
Copy link
Contributor

Closes #47

@thor314
Copy link
Contributor

thor314 commented Aug 16, 2024

implemented tests and logic for ghash and polyval mulx component.

had to take special care while implementing polyval to correctly implement little-endian byte-shifting.

There's a concerning conflict between rust-crypto and ietf's tests in polyval.

From IETF we implement:

    // ref: https://datatracker.ietf.org/doc/html/rfc8452#appendix-A
    it("compute IETF test 2", async () => {
      let bits = hexToBitArray("9c98c04df9387ded828175a92ba652d8");
      let expect = "3931819bf271fada0503eb52574ca5f2";
      ...

and from rustcrypto we implement a conflicting test case. Not sure what to do here.

repro:
just circom-testg polval

currently prioritizes passing the rust-crypto impl as it looks plausibly more correct. I've left a comment in the circom:

    for (var i = 0; i < 128; i++) {
        // if (i==7 || i == 120 || i==121 || i==126) { // passes rust-crypto
        if (i==7 || i==121 || i==126) { // passes ietf spec?

switching these lines seems clearly logically wrong, as it would encode the wrong polynomial for polyval, but switching between them allows for different tests to pass.

I did check that I copied the correct strings from the IETF spec, but would appreciate a second pair of eyes.

recall that little endian is a bit annoying to work with, making polyval more annoying to implement than Ghash (which is implemented and complete with no weirdness).

example:
In binary and hexadecimal, with byte endings denoted by semicolon, for the 2-byte value 33,825 (the sum of $2^0,2^5,2^{10},2^{15}$):

  • Big endian: 33825 = 1000 0100; 0010 0001 = 0x84 0x21
  • Little endian: 33825 = 0010 0001; 1000 0100 = 0x21 0x84

resolved:
https://github.com/RustCrypto/universal-hashes/blob/master/polyval/src/mulx.rs#L27

NOTE: the vector in the RFC actually contains a typo which has been
reported (and accepted) as RFC errata, so we use the vector from the
errata instead:

@0xJepsen
Copy link
Contributor Author

Amazing work on this. Weird that the RFC has a type in it. Good find on that. I am down to merge this in it looks really nice and has good documentation. Thank you for setting a nice standard on quality here, the work is beautiful and speaks for itself.

@0xJepsen 0xJepsen merged commit 516ba51 into main Aug 16, 2024
2 checks passed
@thor314 thor314 mentioned this pull request Aug 19, 2024
for (var i = 1; i < 128; i++) { v[i] <== in[i-1]; }

// if MSB set, assign irreducible poly bits, otherwise zero
// irreducible_poly has 1s at positions 1, 2, 7, 127
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this be
at positions 1, 2, 7, 128

// ...1100 0010 <== encodes 121, 126, 127
// ...0100 0010 <== encodes 121, 126
for (var i = 0; i < 128; i++) {
if (i==7 || i == 120 || i==121 || i==126) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Be more explicit about LE stuff

// mid2= [0 a b c d e f g, h i j k l m n o] // shift bits right by 1
// out = [g f e d c b a 0, o n m l k j i h] // swap order of bits in each byte
// TODO(TK 2024-08-15): optimize
template LeftShiftLE(shift) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Very readable. Don't optimize until necessary

it("should have correct output", async () => {
const witness = await circuit.calculateWitness({ in: bit_array });
const witness = await circuit.expectPass({ in: bit_array}, { out: expected_output });
circuit.expectPass({in: bit_array});
Copy link
Collaborator

Choose a reason for hiding this comment

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

I recommend that tests get back the output and compare that to expected output instead of using expectPass to be consistent with the expressiveness of most testing frameworks.

const expect = ...
let result = await circuit.compute(input, ["out"]);
assert.equal(result, expected);

If not, remove the second circuit.expectPass and remove printing out the witness below.

import { bitArrayToHex, circomkit, hexToBitArray } from "../common";

// Disable truncation of arrays in error messages
chai.config.truncateThreshold = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Better to have this in a single test-setup file than at the start of each file imo.
We can create a test/setup.ts file, then either include it with --require ./test/setup.js or in mocha.opts (Which is better option)

@0xJepsen 0xJepsen deleted the gf_mulx branch September 9, 2024 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat: gmulx
3 participants