-
Notifications
You must be signed in to change notification settings - Fork 12
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
feat: implement GroupNoble #38
Conversation
Thanks!
Will sort when on keyboard
|
I added some tsconfig.cjs.json files, and added them as a reference to the root tsconfig.json. I also note that this is declaring "type": "module" in the package.json, which, due to node module resolution means any consumers will have to be using ESM, regardless of any "exports" configuration:
I tried neutralising the package.json by removing "type":"module", however jest started failing as it didn't know to interpret .js files as ESM:
I will try again later, but if you have any ideas? Thinking out loud: |
Ok, I've got something that seems to work. I removed "type" : "module" from the root package.json and committed lib/{esm,cjs}/package.json files setting their respective "type"s. At the moment the test/build commands simply build and test both cjs and esm at once (edit: added commands for faster specific tests). SCJL is the default group, and the tests can be configured to test noble by setting the environment variable
|
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.
Hi, here are some suggested changes.
src/groupNoble.ts
Outdated
compat(this, k2) | ||
compat(this, a) | ||
const el = this.p.multiplyAndAddUnsafe(a.p, k1.k, k2.k) | ||
if (!el) throw new Error('result is zero') |
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.
it shouldn't make a special case returning the ZERO point.
For example this is valid call to this function:
P = gen()
Q = P
a = order-1
b = 1
R = a*P+b*Q
= (order-1)*P + P
= -P+P
= 0
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.
/**
* Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
* Not using Strauss-Shamir trick: precomputation tables are faster.
* The trick could be useful if both P and Q are not G (not in our case).
* @returns non-zero affine point
*/
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
const G = Point.BASE; // No Strauss-Shamir trick: we have 10% faster G precomputes
const mul = (
P: Point,
a: bigint // Select faster multiply() method
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
const sum = mul(this, a).add(mul(Q, b));
return sum.is0() ? undefined : sum;
}
It seems multiplyAndAddUnsafe returns a non zero Point
This one is probably better fielded by @paulmillr
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.
For the moment I'm just returning a zero point when multiplyAndAddUnsafe returns undefined (the point was zero)
Any better ideas ?
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.
it seems to me that this need to be solved in the noble package.
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.
The workaround should be OK to get things moving though, no?
src/groupNoble.ts
Outdated
return EltNb.gen(this).mul(s) | ||
} | ||
|
||
async randomScalar(): Promise<ScalarNb> { |
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.
no need to use async as no await
is in the body of this function.
async randomScalar(): Promise<ScalarNb> { | |
randomScalar(): Promise<ScalarNb> { |
same applies to the other methods
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.
Some linters actually enforce consistent use of async, which is something I can get behind, but if that's the convention here, I can go with it. Maybe there's a linting rule this project could set up ?
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.
async randomScalar(): Promise<ScalarNb> {
const msg = crypto.getRandomValues(new Uint8Array(this.size))
return ScalarNb.hash(this, msg, new Uint8Array())
}
Actually, looking at this more, I can see that the async modifier is what makes the simple return value
upgrade to a Promise, rather than having to use Promise.resolve(value)
Our Group interfaces are async (using Promises), so the async is actually required?
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.
until there is a linter rule enabled to get this uniformly, I consider that using async
only when inside the body of the function there is an await
keyword.
A function is known to be async iff it returns a Promise
.
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.
A function is known to be async iff it returns a Promise.
Let's dig into this a bit more.
The randomScalar method of our Group interface is asynchronous so that it can support the sjcl/webcrypto (used for expandMD) implementation.
scalarNb.hash(this, msg, new Uint8Array())
This is a sync operation, and not a promise, it's true, but by putting async
on the function it "upgrades" the return value to a Promise so it will conform to our interface.
The alternative here is to use Promise.resolve(), which I will do:
randomScalar(): Promise<ScalarNb> {
const msg = crypto.getRandomValues(new Uint8Array(this.size))
return Promise.resolve(ScalarNb.hash(this, msg, new Uint8Array()))
}
I found it helpful to be able to target a specific group for performance
sake. These tests can be quite slow. Maybe there is a middle ground here.
Am currently on a trip(and on mobile), though back soon.
Thanks.
|
I've addressed your concerns/comments. I've left the Thanks! |
src/groupNoble.ts
Outdated
return EltNb.gen(this).mul(s) | ||
} | ||
|
||
async randomScalar(): Promise<ScalarNb> { |
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.
until there is a linter rule enabled to get this uniformly, I consider that using async
only when inside the body of the function there is an await
keyword.
A function is known to be async iff it returns a Promise
.
src/groupNoble.ts
Outdated
eltDes: Deserializer<EltNb> = { | ||
size: (compressed?: boolean) => { | ||
return this.eltSize(compressed) | ||
}, | ||
deserialize: (b: Uint8Array) => { | ||
return this.desElt(b) | ||
} | ||
} | ||
scalarDes: Deserializer<ScalarNb> = { | ||
size: () => { | ||
return this.scalarSize() | ||
}, | ||
deserialize: (b: Uint8Array) => { | ||
return this.desScalar(b) | ||
} | ||
} |
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.
bump
@sublimator please rebase and squash your commits, so it can be merged. |
I got conflicts when I tried to rebase, so I just merged origin/main, so it should squash down from the Github UI easily. Only merge commits allowed? |
7ccccb2
to
99e19c1
Compare
Ok, I created a new commit on top of main from the contents of the old commits and force pushed. |
Thank for your contributions @sublimator |
Wooo! Thanks
…On Sat, 2 Sep 2023 at 7:04 AM Armando Faz ***@***.***> wrote:
Thank for your contributions @sublimator <https://github.com/sublimator>
—
Reply to this email directly, view it on GitHub
<#38 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAEAHG64XUJ476C6I6PP34LXYJZ2JANCNFSM6AAAAAAZNF5HXI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Continuing on with #26
Also deals with #30
Scripts
npm test
GROUP_CONS=noble npm test
npm run test:esm
GROUP_CONS=noble npm run test:esm
npm run test:cjs
GROUP_CONS=noble npm run test:cjs
Notes
declare
function and uses describe.eachnew
constructor (eye to short vs ristretto/decaf448 etc)