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

Use WebCrypto instead for performance & bundle size improvements #464

Closed
memcorrupt opened this issue Feb 5, 2024 · 3 comments
Closed
Labels

Comments

@memcorrupt
Copy link

memcorrupt commented Feb 5, 2024

See #296 for original issue.


This package uses jsSHA as a dependency, which adds 21.4 kB to the bundle size (ESM minified). This is ~77.4% of this module's bundle size.

The WebCrypto API (including SubtleCrypto) is widely supported in browsers (while using HTTPS), and provides native implementations of crypto functions. Additionally, it supports all the currently supported algorithms in hmac-digest.js except SHA-224; however, only SHA-1, SHA-256, and SHA-512 are actually specified in the TOTP RFC.

Additionally, because the library's dependencies are currently bundled, jsSHA may be unnecessarily bundled in a final project multiple times if dependent projects contain files or dependencies that require jsSHA.

Since Web Crypto API is asynchronous only, this improvement could either be implemented as a breaking change, or with seperate asynchronous generate/validate methods that include the Web Crypto implementation.

@hectorm
Copy link
Owner

hectorm commented Feb 12, 2024

What I initially stated in the linked issue still applies, I prefer not to use the Web Crypto API if it means breaking compatibility, since jsSHA, while not native, is a widely used SHA implementation that works well.

But there is room for improvement here, the first thing would be to study the feasibility of creating an alternative build that uses the Web Crypto API (although it would not be trivial as this build would have to expose a slightly different API since it would have to use asynchronous methods) and the second thing would be to have a build that does not bundle jsSHA to avoid it being included multiple times in case another dependency imports it.

Right now this is not a priority for me, but I leave this issue open to review it in the future, a PR would also be welcome.

@memcorrupt
Copy link
Author

@hectorm I had started some progress here, but I wasn't quite sure how to produce a WebCrypto build & a jsSHA build to maintain the browser compatbility required to upstream the changes.

If this is a good start, you could possibly finish it or provide the insight needed to continue.

@hectorm
Copy link
Owner

hectorm commented May 29, 2024

After a benchmark with jsSHA, @noble/hashes and your SubtleCrypto fork, I noticed that the latter is much slower (possibly due to the overhead of the async function), so I don't think I will use the Web Crypto API for the time being.

Although thanks to this I'm thinking about switching to @noble/hashes as it would reduce the minified bundle size from 30 KB to 24 KB (without compression). The idea of providing a variant that doesn't bundle the HMAC library to avoid duplication still stands, but that's outside the scope of this issue, so I'll close this one and create another.

Bun 1.1.10:

Task Name ops/sec Average Time (ns) Margin Samples
totpValidate 55 18172903.26860257 ±0.29% 551
totpNobleValidate 103 9625471.101058695 ±1.04% 1039
totpSubtleValidate 23 42465075.48305099 ±2.07% 236

Deno 1.43.5:

Task Name ops/sec Average Time (ns) Margin Samples
totpValidate 74 13454301.075268818 ±0.85% 744
totpNobleValidate 73 13608163.265306123 ±1.55% 735
totpSubtleValidate 19 51517948.71794872 ±0.81% 195

Chromium 125:

Task Name ops/sec Average Time (ns) Margin Samples
totpValidate 82 12078045.838318126 ±0.57% 829
totpNobleValidate 71 14077918.424788447 ±0.60% 711
totpSubtleValidate 8 122246341.46336012 ±2.72% 82

Firefox 126:

Task Name ops/sec Average Time (ns) Margin Samples
totpValidate 62 16059390.048154093 ±0.66% 623
totpNobleValidate 39 25173366.834170856 ±0.92% 398
totpSubtleValidate 11 88796460.17699115 ±7.37% 113
Source
import { Bench } from "tinybench";
import * as otpauth from "otpauth";
import * as otpauthNoble from "otpauthNoble";
import * as otpauthSubtle from "otpauthSubtle";

(async () => {
  const bench = new Bench({
    time: 10000,
    warmupTime: 1000,
  });

  const totp = new otpauth.TOTP({ secret: "NB2W45DFOIZA" });
  bench.add("totpValidate", () => {
    totp.validate({ token: "000000", window: 1000 });
  });

  const totpNoble = new otpauthNoble.TOTP({ secret: "NB2W45DFOIZA" });
  bench.add("totpNobleValidate", () => {
    totpNoble.validate({ token: "000000", window: 1000 });
  });

  const totpSubtle = new otpauthSubtle.TOTP({ secret: "NB2W45DFOIZA" });
  bench.add("totpSubtleValidate", async () => {
    await totpSubtle.validate({ token: "000000", window: 1000 });
  });

  await bench.warmup();
  await bench.run();
  console.table(bench.table());
})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants