2

The Node.js crypto module provides a cipher.getAuthTag() implementation that returns a Buffer which contains the authentication tag after the data has been successfully encrypted.

The SubtleCrypto.encrypt() Web Crypto API method supports the AES-GCM encryption algorithm, but has no documentation on how to retrieve the authentication tag like in Node.js. Is this possible with the Web Crypto API? If so, how?

ollog10
  • 91
  • 2
  • 8
  • 1
    Some Frameworks provide a separate GCM-tag and others add the GCMtag at the end of the ciphertext (e.g. Java and WebCrypto), so just cut it off from the ciphertext like "var dataGcmTag = new Uint8Array(dataBuf.slice((dataBuf.length-16), dataBuf.length));". Don't forget to add it to the ciphertext before decrypting in WebCrypto... – Michael Fehr Oct 30 '22 at 07:06
  • Note the [AesGcmParams](https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams) dictionary – Ronnie Royston Aug 01 '23 at 19:36

2 Answers2

4

After some extensive internet research, I discovered that the Web Crypto encryption method appends the auth tag to the resulting ciphertext. By default, its length is 16 bytes, and can be extracted from the payload with array manipulation:

const nonce = crypto.getRandomValues(new Uint8Array(12));
const aes_key = await crypto.subtle.generateKey(
    {
        name: 'AES-GCM',
        length: 256,
    },
    true,
    ['encrypt', 'decrypt']
);
const encrypted = await crypto.subtle.encrypt(
    {
        name: 'AES-GCM',
        iv: nonce,
    },
    aes_key,
    new Uint8Array() //plaintext to be encrypted
);

const [value, auth_tag] = [
    encrypted.slice(0, encrypted.byteLength - 16),
    encrypted.slice(encrypted.byteLength - 16),
];
//value will be ArrayBuffer of ciphertext
//auth_tag will be ArrayBuffer of the authentication tag
ollog10
  • 91
  • 2
  • 8
  • I created a nice example that shows how to integrate Web Crypto API and Node.js Crypto API, you might want to check it out: https://replit.com/@VictorQueiroz/FlakyCarelessBuckets – Victor Queiroz May 22 '23 at 18:07
0

See AesGcmParams for additional info on the .encrypt method specific to GCM mode. The default length of the authTag is 128 bits, or 16 bytes. See the W3C Spec. The output of this encryption process is a byteArray. Therefore JavaScript would look like:

https://www.w3.org/TR/WebCryptoAPI/#aes-gcm-operations

cipherText = await crypto.subtle.encrypt(
  {
    name: "AES-GCM",
    iv: iv,
    additionalData: jweProtectedHeader, // optional
    tagLength: 128 //default
  },
  contentEncryptionKey,
  payload
);
let authTagLengthBytes = tagLength / 8;
let authTagBytes = cipherText.slice(cipherText.byteLength - authTagLengthBytes,cipherText.byteLength);
let authTagString = new TextDecoder().decode(uint8array);
Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91