2

According to section 5.2 (Two GCM Functions) of the Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC, it mentions that for the case of GMAC, the authenticated encryption and decryption functions become the functions for generating and verifying an authentication tag on the non-confidential data.

Looking online and on the JCA reference page, I understand that GMAC is generated like this:

Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
cipher.init(ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
cipher.updateAAD(aadData);
byte[] gmac = cipher.doFinal();

However, I have doubts regarding the GMAC verification. Is this the right way of verifying a GMAC?

Cipher decryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
decryptCipher.init(DECRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
decryptCipher.updateAAD(aadData);
decryptCipher.update(gmac);
byte[] verifiedGmac = decryptCipher.doFinal();

Where verifiedGmac size == 0?

Here is the full code with inputs and outputs:

import org.hamcrest.core.Is;
import org.junit.Test;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;

import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static javax.xml.bind.DatatypeConverter.parseHexBinary;
import static javax.xml.bind.DatatypeConverter.printHexBinary;
import static org.junit.Assert.assertThat;

public class TestGmac {

    @Test
    public void generateAndVerifyGmac() throws Exception {
        final byte[] message = parseHexBinary("AAAAAAAAAAAA");
        final byte[] authenticatedKey = parseHexBinary("63509E5A672C092CAD0B1DC6CE009A61");
        final byte[] aadData = buildAadForAuthenticationOnly(message, authenticatedKey);
        final byte[] iv = parseHexBinary("BBBBBBBBBBBBBBBBBBBBBBBB");
        SecretKeySpec encryptionKey = new SecretKeySpec(parseHexBinary("55804F3AEB4E914DC91255944A1F565A"), "AES");

        // Generate GMAC
        Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
        cipher.init(ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
        cipher.updateAAD(aadData);
        byte[] gmac = cipher.doFinal();
        assertThat(printHexBinary(gmac), Is.is("44C955D63799428524E97993"));

        // Verify GMAC
        Cipher decryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
        decryptCipher.init(DECRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
        decryptCipher.updateAAD(aadData);
        decryptCipher.update(gmac);
        byte[] verifiedGmac = decryptCipher.doFinal();
        assertThat(printHexBinary(verifiedGmac), Is.is(""));
    }

    private byte[] buildAadForAuthenticationOnly(byte[] message, byte[] authenticatedKey) throws IOException {
        ByteArrayOutputStream aaDoutputStream = new ByteArrayOutputStream();
        aaDoutputStream.write(parseHexBinary("10"));
        aaDoutputStream.write(authenticatedKey);
        aaDoutputStream.write(message);
        return aaDoutputStream.toByteArray();
    }

}
ktulinho
  • 3,870
  • 9
  • 28
  • 35

1 Answers1

1

Yes, that is correct. The Java API verifies the MAC, throwing an exception if it fails. It returns the encrypted plaintext message, but in your case the message is empty.

There are two design choices for creating a GCM API: handle the authentication tag separately or assume it is part of the ciphertext. Although I prefer the separate handling of ciphertext and authentication tag, Java/Oracle decided to keep to the AEAD RFC and include the authentication tag with the ciphertext (requiring extra buffering, a code size increase of about 30% and removing the posiblity to perform online decryption).

So although your code maybe feels a bit strange the handing of the MAC seems OK - if it weren't you'd get a AEADBadTagException in the call to doFinal during decryption.


Using a static key and IV certainly isn't OK, but I presume those were used for testing purposes - right?

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Thanks for your answer! Yes, this is just test code! I wanted everything ion one place so that I could post the question here and it would be easier for people to understand what I was trying to achieve :) Regarding the structure of the code, what would be a best practice? – ktulinho Feb 01 '18 at 09:20
  • 1
    Ooh, that's a bit broad. You can at least have a look at [this answer](https://stackoverflow.com/a/15712409/589259), although it hasn't been expanded to authenticated cipher modes such as GCM yet. Don't reuse stateful objects such as `Cipher` instances unless you have to; just share or keep the key object (if you have to use it again). – Maarten Bodewes Feb 01 '18 at 09:35