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

Migrating/Integrating WebCrypto (possibly WASM) instead of Node Forge Crypto #270

Closed
CMCDragonkai opened this issue Oct 26, 2021 · 10 comments · Fixed by #446
Closed

Migrating/Integrating WebCrypto (possibly WASM) instead of Node Forge Crypto #270

CMCDragonkai opened this issue Oct 26, 2021 · 10 comments · Fixed by #446
Assignees
Labels
development Standard development epic Big issue with multiple subissues r&d:polykey:core activity 2 Cross Platform Cryptography for JavaScript Platforms

Comments

@CMCDragonkai
Copy link
Member

Specification

The work in #155 involving nativescript meant that we are starting to consider alternatives to Node forge crypto in order to standardise our crypto API and future proof to future JS-based platforms. There are number of reasons to look into webcrypto:

  • WebCrypto is a standard across ES platforms, thus enabling more potential native-implementations which can improve performance and security (prevent timing attacks)
  • WebCrypto API focuses on using ES-compliant buffers like Uint8Array and ArrayBuffer which can help us standardise our buffer usage across js-db, js-id, js-workers and more, especially as workers require ArrayBuffer to do zero-copy
  • There is existing work with WebCrypto API involving WASM to plug in functionality that isn't supported by the standard, such as providing ed25519 keys, and this will help us resolve the usage of ed25519 Replace node-forge RSA Keypair With ed25519/x25519 Keypair #168
  • The node-forge source code isn't well maintained and doesn't have as many eyes watching and evaluating its security

Now for some background:

This comment #43 (comment) explains how we came to be using node-forge as opposed to other cryptographic libraries.

I know that when Polykey project first started, we initially were thinking of using PGP and thus the kbpgp.js library (https://github.com/keybase/kbpgp). We ended up going away from PGP due to its limited usage in a number of scenarios that we want PK to deal with. Namely end to end encrypted network communication which is a TLS issue, that makes use of X.509 certificates rather than PGP certificates. Furthermore we also had symmetric encryption/decryption scenarios like js-encryptedfs that again would not make use of PGP standards. Therefore it just lacked interoperability with many other cryptographic scenarios, it seemed like its own island of standards, the library can still be brought back in in the future if we find usecases for PK using PGP.

However in choosing node-forge, we came across a few other problems. Mainly overall-cross platform compatibility planning for mobile devices. This is not just a problem with crypto, but also other libraries that are used in our networking domain such as utp-native.

Here are something I found that may be relevant to us proceeding here:

All of this will mean that we either replace node-forge, or end up creating a adapter pattern where we plugin different crypto implementions depending on our environment. At this point in time, the keys domain abstracts over most(all?) crypto operations for all other domains. Except in the case of EFS which is currently pinned to node-forge (it may be a good idea to abstract that and expect an interface of functions for EFS).

Cross platform compatibility here isn't just about the fundamental crypto library. It's also about other parts of PK. One closely related situation is the JOSE libraries. As they involve cryptographic operations, they currently seem to "fix" their underlying crypto library as well. It would be ideal that if we standardise on a crypto library for cross-platform deployment, that we can also ensure that our JOSE library is using the same crypto library to reduce our crypto attack surface. We are currently using https://github.com/panva/jose which uses native crypto depending on the platform including webcrypto. Contenders include https://github.com/cisco/node-jose (which fixes on node-forge) and https://github.com/square/js-jose.

Additional context

Wasmer can compile WASM code to native code. But wasm3 is for interpretation. Why use interpetation?

It appears that in some cases interpretation can be more widely deployed. There are examples of iOS apps using wasm3. https://github.com/kateinoigakukun/wasmic-ios

It's becoming fast a standard target for many languages. Even TypeScript when ported to AssemblyScript can be compiled to WASM.

Once it is WASM, the only thing missing is broad adoption of WASI. If WASI is broadly adopted like it is in nodejs (https://github.com/nodejs/uvwasi), then pretty much we have a universal portable binary capable of doing relevant system operations. WASI is like a universal standard of system calls. Like a whole new POSIX standard.

Then one would just use WASM and WASI for all platforms.

Tasks

  1. ...
  2. ...
  3. ...
@joshuakarp
Copy link
Contributor

Note that we currently depend on crypto within the sigchain with our usage of the JWS in panva-jose. We use it to both sign and verify a claim (requiring asymmetric cryptography, which in turn requires generating a KeyLike from the PEM-formatted public and private key). This would need to be refactored in order to migrate away from crypto.

Original discussion of this can be found here https://gitlab.com/MatrixAI/Engineering/Polykey/js-polykey/-/merge_requests/213#note_736387549

@CMCDragonkai
Copy link
Member Author

Note that age and wireguard both use https://en.m.wikipedia.org/wiki/ChaCha20-Poly1305.

It's an alternative to AES GCM which has higher performance when the hardware doesn't support AES-NI instructions.

AES GCM is still good for compliance though, some places like government require this.

But the algorithm apparently works and is designed for software-only implementations.

@CMCDragonkai
Copy link
Member Author

We will need to elevate this issue to a higher priority, we now have empirical results on how long it takes to generate a root key in multiple arenas:

  1. Tests
  2. AWS fargate

On fargate, using the 0.25 CPU containers, it takes 20 minutes to generate a root key pair. Se can be seen by this cloudwatch cpu utilisation graph:

cpu

At the same time, key generation is using all cores by default. This doesn't even require the worker manager to be integrated, I think node-forge does this by default.

We believe that this is causing CPU starvation of all the other testing workers that jest creates and thus leading to test timeouts as can be seen in #394.

As we have deployed our testnet, and we expect the need to autoscale the agents, key generation is going to be an important workload, as it delay the scaling up process. Remember even if we pass in a recovery code, the keys must still be generated and this still takes time.

Furthermore we want to get #168 done before going to mainnet, because at that point users will be using our system, and we don't want to get stuck on legacy RSA.

@CMCDragonkai
Copy link
Member Author

@tegefaulkes @emmacasolin

@tegefaulkes
Copy link
Contributor

It's possible to override the number of workers that the generateKeyPair src/keys/utils.ts:64 uses. We can provide workers: 1 as part of the options.

@CMCDragonkai CMCDragonkai added the r&d:polykey:core activity 2 Cross Platform Cryptography for JavaScript Platforms label Jul 24, 2022
@CMCDragonkai
Copy link
Member Author

CMCDragonkai commented Aug 8, 2022

The node-forge has its own workers implementation it doesn't actually use WorkerManager. We are looking into refactoring WorkerManager with respect to the Queue and Scheduler implementation in #329. And replacing the crypto is important for our (second, our first one was back in around Jan - Mar 2021 which failed) beta launch, because we want users to be using #168.

So this is good opportunity to take advantage of some intersectionality between crypto, workers, and task abstraction.

@CMCDragonkai
Copy link
Member Author

CMCDragonkai commented Aug 31, 2022

Note that 16.17 has proper support for ed25519:

image

Current latest 22.05 is still 16.16.

Will require going to unstable if we want 16.17's version of ed25519.

@CMCDragonkai
Copy link
Member Author

The commit that introduced 16.17 into nodejs is 6e2536f1b09863e984f0479ea4b162e4fe86493d.

To use it, one must use nodejs-16_x as nodejs will be 18.9.0 which may have some major differences.

I'm going to try the latest master commit and see how we go. This will impact all of the nix-derived dependencies.

@CMCDragonkai
Copy link
Member Author

CMCDragonkai commented Oct 5, 2022

There is 7 points remaining for this epic (since they had work done already).

@CMCDragonkai
Copy link
Member Author

The benchmark results in #446 means that we do need webcrypto and due to iOS issues, we cannot use wasm. So we have moved to libsodium instead, and the benchmarks show that libsodium is 10x to 50x faster than webcrypto.

We are also going with native Buffer, and future usage will just use the Buffer polyfill.

There remains 2 uses of webcrypto:

  1. bip39 recovery code
  2. x509 signing

The first can be replaced with our own KDF mechanism. In that sense, we would no longer use bip39.

The second is a bit more difficult, it may be easier to create a webcrypto shim backed off libsodium and only for ed25519 signing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
development Standard development epic Big issue with multiple subissues r&d:polykey:core activity 2 Cross Platform Cryptography for JavaScript Platforms
Development

Successfully merging a pull request may close this issue.

3 participants