Internal Error when using Web Crypto

Here’s my code:

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

/**
 * Fetch and log a given request object
 * @param {Request} request
 */
async function handleRequest(request) {
    var createdCryptoKey = await crypto.subtle.generateKey(
    {
        name: "AES-GCM",
        length: 256, //can be  128, 192, or 256
    },
    false, //whether the key is extractable (i.e. can be used in exportKey)
    ["encrypt", "decrypt"] //can "encrypt", "decrypt", "wrapKey", or "unwrapKey"
)
console.log(createdCryptoKey);
  var encryptedData = await crypto.subtle.encrypt(
    {
        name: "AES-GCM",
        iv: crypto.getRandomValues(new Uint8Array(12)),
        tagLength: 128,
    },
    createdCryptoKey, //from generateKey or importKey above
    new ArrayBuffer("test") //ArrayBuffer of data you want to encrypt
  );
  console.log("encrypted:", encryptedData);
  return new Response("test")
}

The error returned is:

Uncaught (in promise) DOMException {}
Uncaught (in response) Error: internal error

This isn’t really that useful, so I don’t know what I’m doing wrong.

Does anyone have any idea what the issue is? AES-GCM appears to be supported for encryption, so I’m not sure what I’m doing wrong.

Thanks in advance.

Make sure to await the createCryptoKey and also crypto.getRandomValues or you’ll retrieve empty values.

That shouldnt be the issue here however. The OP does use await in the generateKey() context and getRandomValues() is not an asynchronous function.

You’re right about generateKey() I saw it wrong, it’s in the same function. I might be wrong about getRandomValues(), the docs doesn’t specify if returns a promise so I assumed that it would work like all the other subtle encryptions and return a promise.

Edit: crypto.getRandomValues() instanceof Promise says noo, so not a promise.

I figured it out.

The string that’s being encrypted has to be encoded using TextEncoder.

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

/**
 * Fetch and log a given request object
 * @param {Request} request
 */
async function handleRequest(request) {
    let enc = new TextEncoder();
    var createdCryptoKey = await crypto.subtle.generateKey(
    {
        name: "AES-GCM",
        length: 256, //can be  128, 192, or 256
    },
    false, //whether the key is extractable (i.e. can be used in exportKey)
    ["encrypt", "decrypt"] //can "encrypt", "decrypt", "wrapKey", or "unwrapKey"
)
console.log(createdCryptoKey);
  var encryptedData = await crypto.subtle.encrypt(
    {
        name: "AES-GCM",
        iv: crypto.getRandomValues(new Uint8Array(12)),
        tagLength: 128,
    },
    createdCryptoKey, //from generateKey or importKey above
    enc.encode("test")
  );
  console.log("encrypted:", encryptedData);
  return new Response("test")
}
1 Like

Indeed, new ArrayBuffer("test") coerces "test" to NaN, creating a zero-length buffer. So it seems like our implementation of SubtleCrypto.encrypt() doesn’t properly handle zero-length plaintext buffers. I don’t think this should throw anything at all, much less the cryptic OperationError/internal error that you’ve encountered, and have filed an internal bug report.

Thank you for reporting this!
Harris

1 Like

Excellent catch!