Decryption of app context with Web Crypto API

Zoom Apps Configuration
NextJS Edge Function (middleware) (probably not revelant)

I’ve managed to successfully employ the NodeJS example for decrypting the x-zoom-app-context header, but I’m struggling to port this to Web Crypto API.

“Unsupported state or unable to authenticate data”

I use the same unpack function found here, and have confirmed that between NodeJS Crypto and Web Crypto API the same bytes are passed in for all parameters:

async function decryptCtxWithHash(cipherText, hash, iv, aad, tag) {
  const key = await subtle.importKey("raw", hash, "aes-gcm", false, [
  const deciphered = await subtle.decrypt(
    { name: "AES-GCM", iv, additionalData: aad, tagLength: tag.byteLength * 8 },
  ); //Fails with "Unsupported state or unable to authenticate data"
  const value = deciphered.slice(0, deciphered.byteLength - tag.byteLength);
  return JSON.parse(new TextDecoder().decode(value));

The key seems to be imported correctly too

CryptoKey {
  type: 'secret',
  extractable: false,
  algorithm: { name: 'AES-GCM', length: 256 },
  usages: [ 'decrypt' ]

@pbowen Does using a different encoding help to work around this issue?

Here’s an example of stack overflow:

Also you can see another implementation of the unpack function in the Basic Sample App.

Out of curiosity, what is the use case for checking this header client side? Are you looking to use the getAppContext() function in a serverless context?

Hi, Max, I’m unsure which encoding you’re referring to?

Like I said, the unpack function is fine, and I managed to decrypt the header in NodeJS just fine, but NextJS Edge Functions only have Web Crypto API available, which is a preferred position in our stack rather than a NodeJS API.
The exact same bytes go into that decryptCtxWithHash function for NodeJS and NextJS, but the NextJS one fails with that error.

(Apologies, I didn’t realise there was a reply function before)
Do you have a suggestion for a different encoding I could try?

I tried testing this on my end but I suppose I don’t have a good understanding of how this is configured on your end.

Are you using the Edge runtime to run this logic on the backend or are you attempting to decrypt this on the client side?

I’m working to reproduce this using the Basic Sample App.