From a3feab94ffded6dc19d90fc879223a183338172f Mon Sep 17 00:00:00 2001 From: Harish Annavisamy Date: Thu, 11 Jun 2026 15:49:15 +0530 Subject: [PATCH] Use constant-time comparison for output prefix verification in MAC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arrays.equals() and util.Bytes.equals() exit on the first mismatching byte, leaking timing information in security-sensitive MAC verification paths. Replace with MessageDigest.isEqual() (constant-time) in: - ChunkedHmacImpl.createVerification() — output prefix check - ChunkedAesCmacImpl.createVerification() — output prefix check - LegacyFullMac.verifyMac() — prefix check before rawMac.verifyMac() Consistent with the fix already applied in ChunkedMacVerificationFromComputation (175df91c). Fixes #72. --- .../crypto/tink/mac/internal/ChunkedAesCmacImpl.java | 7 +++++-- .../google/crypto/tink/mac/internal/ChunkedHmacImpl.java | 7 +++++-- .../com/google/crypto/tink/mac/internal/LegacyFullMac.java | 3 ++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/google/crypto/tink/mac/internal/ChunkedAesCmacImpl.java b/src/main/java/com/google/crypto/tink/mac/internal/ChunkedAesCmacImpl.java index aa1525bca..a7c2b3cf4 100644 --- a/src/main/java/com/google/crypto/tink/mac/internal/ChunkedAesCmacImpl.java +++ b/src/main/java/com/google/crypto/tink/mac/internal/ChunkedAesCmacImpl.java @@ -22,10 +22,11 @@ import com.google.crypto.tink.mac.ChunkedMac; import com.google.crypto.tink.mac.ChunkedMacComputation; import com.google.crypto.tink.mac.ChunkedMacVerification; -import com.google.crypto.tink.util.Bytes; import com.google.errorprone.annotations.Immutable; import java.security.GeneralSecurityException; +import java.security.MessageDigest; import java.security.Provider; +import java.util.Arrays; /** AES-CMAC implementation of the ChunkedMac interface. */ @Immutable @@ -52,7 +53,9 @@ public ChunkedMacVerification createVerification(final byte[] tag) if (tag.length < key.getOutputPrefix().size()) { throw new GeneralSecurityException("Tag too short"); } - if (!key.getOutputPrefix().equals(Bytes.copyFrom(tag, 0, key.getOutputPrefix().size()))) { + if (!MessageDigest.isEqual( + key.getOutputPrefix().toByteArray(), + Arrays.copyOf(tag, key.getOutputPrefix().size()))) { throw new GeneralSecurityException("Wrong tag prefix"); } return ChunkedMacVerificationFromComputation.create(new ChunkedAesCmacComputation(key), tag); diff --git a/src/main/java/com/google/crypto/tink/mac/internal/ChunkedHmacImpl.java b/src/main/java/com/google/crypto/tink/mac/internal/ChunkedHmacImpl.java index 9f67d548e..87d1751fb 100644 --- a/src/main/java/com/google/crypto/tink/mac/internal/ChunkedHmacImpl.java +++ b/src/main/java/com/google/crypto/tink/mac/internal/ChunkedHmacImpl.java @@ -21,9 +21,10 @@ import com.google.crypto.tink.mac.ChunkedMacComputation; import com.google.crypto.tink.mac.ChunkedMacVerification; import com.google.crypto.tink.mac.HmacKey; -import com.google.crypto.tink.util.Bytes; import com.google.errorprone.annotations.Immutable; import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.util.Arrays; /** Class that provides the functionality expressed by the ChunkedMac interface with HMAC. */ @Immutable @@ -53,7 +54,9 @@ public ChunkedMacVerification createVerification(final byte[] tag) if (tag.length < key.getOutputPrefix().size()) { throw new GeneralSecurityException("Tag too short"); } - if (!key.getOutputPrefix().equals(Bytes.copyFrom(tag, 0, key.getOutputPrefix().size()))) { + if (!MessageDigest.isEqual( + key.getOutputPrefix().toByteArray(), + Arrays.copyOf(tag, key.getOutputPrefix().size()))) { throw new GeneralSecurityException("Wrong tag prefix"); } return ChunkedMacVerificationFromComputation.create(new ChunkedHmacComputation(key), tag); diff --git a/src/main/java/com/google/crypto/tink/mac/internal/LegacyFullMac.java b/src/main/java/com/google/crypto/tink/mac/internal/LegacyFullMac.java index d1045bbe1..c3960e99d 100644 --- a/src/main/java/com/google/crypto/tink/mac/internal/LegacyFullMac.java +++ b/src/main/java/com/google/crypto/tink/mac/internal/LegacyFullMac.java @@ -28,6 +28,7 @@ import com.google.crypto.tink.proto.OutputPrefixType; import com.google.crypto.tink.subtle.Bytes; import java.security.GeneralSecurityException; +import java.security.MessageDigest; import java.util.Arrays; /** @@ -97,7 +98,7 @@ public void verifyMac(byte[] mac, byte[] data) throws GeneralSecurityException { macNoPrefix = Arrays.copyOfRange(mac, CryptoFormat.NON_RAW_PREFIX_SIZE, mac.length); } - if (!Arrays.equals(identifier, prefix)) { + if (!MessageDigest.isEqual(identifier, prefix)) { throw new GeneralSecurityException("wrong prefix"); }