-
Notifications
You must be signed in to change notification settings - Fork 52
feat: swap js implementation of Ed25519 for native module or wasm in the browser #215
Conversation
When the DHT is enabled, the amount of network traffic nodes send is vastly increased. At that point our crypto implmenetations fast become a performance bottleneck as we are signing and verifying a greatly increased number of messages due to the increased number of peers we have. Pure-js implementations of Ed25519 are just not fast enough for use. Switching to native/WASM versions drops ambient CPU usage for js-IPFS from ~80% to ~2% while DHT table refreshes are ongoing in my local testing. Uses [ed25519](https://www.npmjs.com/package/ed25519) in node, falling back to [ed25519-wasm-pro](https://www.npmjs.com/package/ed25519-wasm-pro) in the browser. Benchmarks: ``` @noble/ed25519 x 191 ops/sec ±24.37% (78 runs sampled) @stablelib/ed25519 x 62.10 ops/sec ±2.14% (73 runs sampled) node-forge/ed25519 x 63.00 ops/sec ±2.62% (71 runs sampled) supercop.wasm x 3,433 ops/sec ±1.77% (83 runs sampled) ed25519-wasm-pro x 3,562 ops/sec ±1.07% (80 runs sampled) ed25519 (native module) x 4,625 ops/sec ±0.96% (84 runs sampled) node.js web-crypto x 3,344 ops/sec ±2.61% (78 runs sampled) ```
…tive-or-wasm-ed25519
How do you know |
This sounds cool, but where exactly does that happen, and how long does I'm asking this because 3.2k signatures per second is pretty fast; basically wondering whether noble-ed is used properly. |
Also note that libp2p/js-libp2p#106 depends on pure-js libs, and cannot use wasm. |
This is a valid concern. If we go the wasm route we'd probably be best off vendoring in the c code and build the blobs during CI. We only need RSA, Ed25519 and secp256k1 for Peer IDs. RSA is available in web-crypto everywhere we support (I think), Ed25519 is pretty small and has a portable version that is easy to build as wasm, and secp256k1 is relatively uncommon on the network so could stay as js for now.
I may have got a bit excited, I'm looking at other places the bottlenecks are so I'm going to mark this as a draft for the time being.
It searches the DHT for peers with KadIDs near to ours and tries to connect to them. This kicks off lots of handshakes which use RSA/Ed25519/secp256k1 to sign outgoing data and verify incoming data. Basically it does what libp2p does, it just causes it to happen a lot more so any inefficiencies become a lot more pronounced.
That's from 2017, I don't know if it's still correct. |
That's still correct, I've talked to Kumavis a few weeks ago. As for
I'm curious how many requests per second are done, once you'll have some time to investigate it! |
Closing as I'm not convinced this is a bottleneck any more. Will re-evaluate as necessary. |
Historically for Lodestar it has been a bottleneck, contributing to 5% of total CPU time in the main thread. 5% puts it in the top 1-3 most expensive ops in a regular beacon node |
Would be great to offer a pure-js option for consumers that don't require very high performance, and a another for high performance consumers. Agree with @paulmillr concerns on security but should be doable to mantain a CI pipeline that builds in-house |
That's a tough part. How would users know the CI hasn't been hacked? We're just two months away since log4j CVE that allowed to break into all sorts of CIs. If you have the real need for ed25519 (is it just your particular project vs all libp2p consumers?), I suggest to compile the rare releases of ed25519 by hand, on your machine. Not on CI. Then, sign it with your GPG keys. Preferably with reproducible builds. There is zero need in having frequent releases of This is how WASM security should be done. |
By the way, if you think i'm saying something theoretical, check out this blog post by NCC group: https://research.nccgroup.com/2022/01/13/10-real-world-stories-of-how-weve-compromised-ci-cd-pipelines/ CI / CD pipeline hacks happen all the time. |
That's a fair point. Tho to attack Lodestar instead of going for the wasm I would just inject some JS that sends the private keys somewhere. That should be doable by attacking the build process of any code that we consume. How do other projects with sensitive users guard against this possibility? |
Steal BLS private staking keys and do what? Launch a beacon node and double-sign some condition, which would slash the validator and lose a couple eths? Seems like a stupid thing to do. No profit in that. |
If you steal enough keys you can ransom the operator. If you don't qualify that as a concern what damage can be done to a beacon node with a malicious WASM payload? |
I agree it's possible and it should be defended against. The ransom can be asked for, but this has never happened yet, and seems like stealing private non-beacon keys from users / breaking defi projects with 300M$ in treasury is more interesting target to attackers. It's not a simple thing to do, but I'd fixate build tool versions, as a first step. Only upgrade for major security release etc. there is no need to run towards new and new deps all the time. Overall, a comprehensive review of build process is required. I don't think security auditors focus on this step? Which is bad. |
Right, the security landscape of protocol vs user layer is very different. We'll do benchmarks first and consider the build infrastructure if necessary. Thanks for the comments tho, appreciate the input |
When the DHT is enabled, the amount of network traffic nodes send is vastly increased. At that point our crypto implementations become a performance bottleneck as we are signing and verifying a greatly increased number of messages due to the increased number of peers we have.
Under these conditions pure-js implementations of Ed25519 are just not fast enough.
Switching to native/WASM versions drops ambient CPU usage for js-IPFS from ~80% to ~2% while DHT table refreshes are ongoing in my local testing.
Uses ed25519 in node, falling back to ed25519-wasm-pro in the browser.
ed25519-wasm-pro
is basically the same as supercop.wasm except the published module is a bit easier to use (no messing around with globalModule['wasmBinary']
values).Benchmarks:
Nb. increases the bundle size by 50kb (though only 5kb bigger than when we used
secp256k1
instead of@noble/secp256k1
)