-
Notifications
You must be signed in to change notification settings - Fork 142
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
Supporting variants of SHA2 #1957
Conversation
}); | ||
|
||
for (let { preimage, hash } of testVectors()) { | ||
let actual = Gadgets.SHA2.hash(384, Bytes.fromString(preimage)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: not sure about the convention, but as a user, reviewing the PR and running the tests, I would like to see some logs here, as it seems it is taking a bit of time to run. I was looking at the stdout for some seconds and stop it as I didn't know what it was running.
(again: not blocking at all)
{ runs: RUNS } | ||
)(nobleSha384, async (x) => { | ||
const { proof } = await Sha2_384Program.sha384(x); | ||
await Sha2_384Program.verify(proof); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: is there a convention to check it also verifies? I added on my side.
const is_verified = await Sha2_384Program.verify(proof);
expect(is_verified);
I don't know if there is another place where we actually test the fact it verifies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.. we are not doing that currently, anyway, but we probably should start doing that for completness altought I am not completely sure if Pickles or Kimchi checks if a proof is valid after generating it as a sanity check already
compression, | ||
messageSchedule, | ||
padding, | ||
initialState<T extends UInt32 | UInt64>(length: Length): T[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to the PR: is there a convention in o1js to order the methods? As an engineer not working often in TS, I was surprised to see the method initialState
defined after a place it is used (hash
above).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really a convention, TS doesn't care - I personally try to order methods by factors such as importance or level of abstraction
// padding the message $5.1.1 into blocks that are a multiple of 512 | ||
let messageBlocks = padding<Type>(length, data); | ||
|
||
let H = SHA2.initialState<Type>(length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: it seems we're doing the Uint32/Uint64 transformations every time we execute hash
. A direct optimization would be to do it once and for all.
Not blocking.
'0'.repeat(k) + // append k zero bits | ||
'0'.repeat(lengthChunk - lBinary.length) + // append 64|128-bit containing the length of the original message | ||
lBinary | ||
).match(/.{1,8}/g)!; // this should always be divisible by 8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh. Is it a common practice in TS? :o
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not common, but it's something I had to do in the other SHA implementation a while ago - cant remember why tho
src/lib/provable/gadgets/sha2.ts
Outdated
// Helper functions | ||
|
||
// Helper function to check if it is a short SHA2 (SHA2-224 or SHA2-256) or not | ||
function isShort(length: Length): boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I would inline this check as isShort
does not have a clear semantics for the primitive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I didn't like it either... At first it was is256 but then it was true also in 224. Any suggestion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly comments at the moment.
I am not approving (sorry for being picky) because I would love to see :
- the origin of the test vectors
- a regression test for the number of constraints
I also think we should therefore get rid of sha256.ts if the primitive is implemented twice.
I have not fully verified that the circuits are not under-constrained.
900b89a
to
bf788f3
Compare
@@ -973,6 +974,27 @@ const Gadgets = { | |||
*/ | |||
SHA256: SHA256, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's deprecate it!
*/ | ||
hash<T extends Length>(length: T, data: FlexibleBytes): Bytes { | ||
// Infer the type T based on the value of `length` (conditional type) | ||
type Type = T extends 224 | 256 ? UInt32 : UInt64; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
damn, some serious typescript! :D
compression, | ||
messageSchedule, | ||
padding, | ||
initialState<T extends UInt32 | UInt64>(length: Length): T[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really a convention, TS doesn't care - I personally try to order methods by factors such as importance or level of abstraction
'0'.repeat(k) + // append k zero bits | ||
'0'.repeat(lengthChunk - lBinary.length) + // append 64|128-bit containing the length of the original message | ||
lBinary | ||
).match(/.{1,8}/g)!; // this should always be divisible by 8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not common, but it's something I had to do in the other SHA implementation a while ago - cant remember why tho
|
||
// Subroutines for SHA2 | ||
|
||
function Ch<T extends UInt32 | UInt64>(length: Length, x: T, y: T, z: T): T { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works well, but if you are interested you could take a look at type guards that might make things in certain cases a bit cleaner
https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
{ runs: RUNS } | ||
)(nobleSha384, async (x) => { | ||
const { proof } = await Sha2_384Program.sha384(x); | ||
await Sha2_384Program.verify(proof); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.. we are not doing that currently, anyway, but we probably should start doing that for completness altought I am not completely sure if Pickles or Kimchi checks if a proof is valid after generating it as a sanity check already
Closes #1941