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();
}
}