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

Decryption Errors #1

Closed
tkcranny opened this issue May 6, 2024 · 1 comment
Closed

Decryption Errors #1

tkcranny opened this issue May 6, 2024 · 1 comment
Assignees

Comments

@tkcranny
Copy link

tkcranny commented May 6, 2024

Hey folks. I've been curious about client-side encryption and have been trying out Minibone.

I've been running into some errors when I call .decrypt() on Minibone instances. This call even fails with different error messages between different browsers, albeit from the same calls.

I'm using a client-side SvelteKit application. Creating Minibone instances, generating the payloads, and encrypting data all work fine, it's just the .decrypt method that seems to be problematic, presumably because of something lower-level in the WebCrypto API.

I've been able to get it down to the below example. I've also made a rudimentary StackBlitz example that replicates it.

const miniboneTest = async (userId: string, secretKey: string, input: string) => {
  const inMb = await Minibone.create();
  const key = await inMb.save(secretKey, ['app.my.domain', userId]);
  const enc = await inMb.encrypt({ input }, key);
  const outMb = await Minibone.load(key, secretKey, ['app.my.domain', userId]);
  const decrypted = await outMb.decrypt(enc); // Crashes here.
};

This crashes on the last .decrypt(call).

If the library isn't intended for frontend applications / browsers, or I'm invoking it incorrectly, then I'm all ears.

Interestingly the error is also different between different browsers, Chrome gives:

Uncaught (in promise) TypeError: Failed to execute 'decrypt' on 'SubtleCrypto': AesGcmParams: additionalData: Not a BufferSource.

Where Safari and Firefox give:

Unhandled Promise Rejection: OperationError: The operation failed for an operation-specific reason.

A screenshot of my playground page for this,
image

@david-alm david-alm self-assigned this Jun 1, 2024
@david-alm
Copy link
Contributor

david-alm commented Jun 1, 2024

Hey @tkcranny. Thanks for filing this issue and taking the time to set up a minimal reproducible example.
You really made debugging this a breeze.

To cut straight to the chase, the immediate fix for your code example is to replace

- const enc = await inMb.encrypt({ input }, key)
+ const enc = await inMb.encrypt(input)

The reason this fails is subtle, but fortunately is expected behaviour.
That said, Minibone should do a better job here and produce helpful error messages.

To delve into the details:
The issue is that the encrypt function accepts an optional associatedData. The same associated data must be passed during decryption, because it's used to compute a message authentication code (a form of symmetric digital signature).

associatedData is used to add context to verify the integrity of data to protect against server-side tampering, replay attacks or other such nefarious modification of the ciphertext by an attacker. The decision to use it depends on your threat model.

In short, another viable fix here would be something like

- const enc = await inMb.encrypt({ input }, key)
+ const enc = await inMb.encrypt(input, userId)

- const decrypted = await outMb.decrypt(enc); // Crashes here.
+ const decrypted = await outMb.decrypt(enc, userId)

Hopefully this clarifies the intended usage.
I'm going to close this issue out, but feel free to open it if you feel it's still unsolved.

(P.S. Congratulations on filing minibone issue #1)

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

No branches or pull requests

2 participants