-
Notifications
You must be signed in to change notification settings - Fork 15
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: upgrade crypto from bcrypto to noble #197
base: master
Are you sure you want to change the base?
Conversation
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.
I would very much love to drop bcrypto everywhere 😄 but curious to hear from @dapplion and @wemeetagain
src/enr/v4.ts
Outdated
|
||
import { NodeId } from "./types.js"; | ||
import { createNodeId } from "./create.js"; | ||
|
||
export function hash(input: Buffer): Buffer { | ||
return keccak.digest(input); | ||
return Buffer.from(keccak(Uint8Array.from(input))); |
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.
Use this api https://nodejs.org/api/buffer.html#static-method-bufferfromarraybuffer-byteoffset-length so that we don't allocate new array but instead just create Buffer view
const ctx = Crypto.createCipheriv("aes-128-gcm", key, iv); | ||
ctx.update(pt); | ||
return ctx.final(); | ||
} | ||
|
||
export async function aesCtrDecrypt(key: Buffer, iv: Buffer, pt: Buffer): Promise<Buffer> { | ||
export function aesCtrDecrypt(key: Buffer, iv: Buffer, pt: Buffer): Buffer { | ||
const ctx = Crypto.createDecipheriv("aes-128-gcm", key, iv); |
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.
still aes-gcm here instead of aes-ctr
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.
Ah, thanks. That's an unused utility but will fix as well.
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.
Actually, it's only aes-ctr in the unauthenticated bits of the handshake and then switches to aes-gcm for authenticated sessions per here
bfdb1dc
to
ad5c5df
Compare
Okay, the cipher piece is fixed. Will look at some benchmarks later today. @dapplion are you thinking mainly encode/decode packets and then sign/verify since that's where the changes in this PR lie? |
I added a benchmark script that uses
|
The pure results on M1 are as such:
Your benchmark also benchmarks other stuff, like |
Yup, I'll leave it to @dapplion and @wemeetagain to decide whether to proceed. The only other optimization we can do would be to remove |
yeah, i'm just saying that even without buffers, your abstraction layer may be too thick, because verification is ~1k ops/sec, not ~500, so it's useful to compare those raw results; they are totally achievable. Some thinking needs to be applied to what are the bottlenecks and at which process stages. |
Haven't had a chance to dig into the benchmarks, but one thing we may be able to do re: the slowdown is make the crypto "pluggable", like we did with noise ChainSafe/js-libp2p-noise#133 Then, for node applications that require highest possible performance, bcrypto or other native implementation can be passed in as configuration. And for browser applications, noble can be passed in. |
Hmm, that's an interesting idea, also how we did for the transport layer. I'm game to rework things down that route, though we'll have to think through the best approach given that it's really 4 different underlying dependencies (secp256k1, sha256, keccak, aes ciphers). To my way of thinking, the aes ciphers is the least challenging one since I just subbed in the node crypto library implementations which seem to be actually slightly faster based on the couple of benchmarks I ran. And, those are simple to polyfill in browser. |
Good point! I'm hoping to avoid spreading this to the ENR layer (I'm hoping to pull it out into its own package at some point) The way it was done in noise was to add some interface (in noise called export interface ICryptoInterface {
hashSHA256: (data: Uint8Array) => Uint8Array
...
} Then something that fulfills that interface gets passed in when the main object is constructed. I guess some decisions could be made about which functions are synchronous vs asynchronous. And which crypto methods need / should be pluggable. |
Make sense. I've already half-way done that by consolidating the secp export in the Would you be good with just letting the aes ciphers stand on their own using the node standard library dependencies? That would simplify things (and I think we can remove those |
For aes, we're using this approach in EF repo: https://github.com/ethereum/js-ethereum-cryptography/blob/master/src/aes.ts It uses node or web browser native built-ins. I'm sure there is zero need in polyfilling AES when web browsers support it. |
I'll take a look. The polyfill approach is nice because we only have to use nodejs builtins API and not maintain separate code branches for the two environments but it also does introduce that secondary dependency (since |
Crypto-browserify is garbage that was last updated in 2017. It's not one dependency - it's at least 12. And I don't think it was thoroughly audited, like our approach. |
@wemeetagain please don't make the API async 🙏 we are already doing enough Promises everywhere |
As long as it's follows roughly what I've done so far in this PR, I shouldn't need to make anything else async that isn't already but will keep that in mind as I work on the interface. |
I'm starting to dig into an interface based approach to the session/keys/crypto stuff and this is kind of what I'm thinking. I'd like to remove the |
@wemeetagain and I met today and talked through this PR and at this point, there doesn't seem like a viable path forward for implementing a crypto interface for the
|
One more note on this. I was finally able to get webpack to find the correct |
I still vote to drop bcrypto at the earliest opportunity but that may be tricky with secp256k1 until you allow for an interface to be passed in. |
b3e0a17
to
938e0fb
Compare
bcrypto
dependency with a combination of@noble/hashes
and@noble/secp256k1
(both are audited, modern, JS only ESM modules with no external dependencies)bcrypto
typeslint:fix
script becauseeslint
is your friend