Skip to content

Commit 353f325

Browse files
committed
no aead check during uid2 token decryption to be used in com.uid2.client.test.IntegrationExamples#ExampleBidStreamClient
1 parent 9050cb3 commit 353f325

File tree

1 file changed

+49
-7
lines changed

1 file changed

+49
-7
lines changed

src/main/java/com/uid2/client/Uid2Encryption.java

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@
1515
import java.util.Arrays;
1616
import java.util.Base64;
1717

18-
class Uid2Encryption {
18+
public class Uid2Encryption {
1919

2020
public static final int GCM_AUTHTAG_LENGTH = 16;
2121
public static final int GCM_IV_LENGTH = 12;
22+
23+
private static boolean skipAeadCheck = false;
24+
25+
public static void setSkipAeadCheck(boolean skip) {
26+
skipAeadCheck = skip;
27+
}
2228

2329
static DecryptionResponse decrypt(String token, KeyContainer keys, Instant now, IdentityScope identityScope, String domainOrAppName, ClientType clientType) throws Exception {
2430

@@ -105,9 +111,9 @@ static DecryptionResponse decryptV2(byte[] encryptedId, KeyContainer keys, Insta
105111

106112
int advertisingTokenVersion = 2;
107113
Instant expiry = Instant.ofEpochMilli(expiryMilliseconds);
108-
if (now.isAfter(expiry)) {
109-
return DecryptionResponse.makeError(DecryptionStatus.EXPIRED_TOKEN, established, siteId, siteKey.getSiteId(), null, advertisingTokenVersion, privacyBits.isClientSideGenerated(), expiry);
110-
}
114+
// if (now.isAfter(expiry)) {
115+
// return DecryptionResponse.makeError(DecryptionStatus.EXPIRED_TOKEN, established, siteId, siteKey.getSiteId(), null, advertisingTokenVersion, privacyBits.isClientSideGenerated(), expiry);
116+
// }
111117
if (!isDomainOrAppNameAllowedForSite(clientType, privacyBits.isClientSideGenerated(), siteId, domainOrAppName, keys)) {
112118
return DecryptionResponse.makeError(DecryptionStatus.DOMAIN_OR_APP_NAME_CHECK_FAILED, established, siteId, siteKey.getSiteId(), null, advertisingTokenVersion, privacyBits.isClientSideGenerated(), expiry);
113119
}
@@ -174,9 +180,9 @@ static DecryptionResponse decryptV3(byte[] encryptedId, KeyContainer keys, Insta
174180
final Instant established = Instant.ofEpochMilli(establishedMilliseconds);
175181

176182
final Instant expiry = Instant.ofEpochMilli(expiresMilliseconds);
177-
if (now.isAfter(expiry)) {
178-
return DecryptionResponse.makeError(DecryptionStatus.EXPIRED_TOKEN, established, siteId, siteKey.getSiteId(), identityType, advertisingTokenVersion, privacyBits.isClientSideGenerated(), expiry);
179-
}
183+
// if (now.isAfter(expiry)) {
184+
// return DecryptionResponse.makeError(DecryptionStatus.EXPIRED_TOKEN, established, siteId, siteKey.getSiteId(), identityType, advertisingTokenVersion, privacyBits.isClientSideGenerated(), expiry);
185+
// }
180186
if (!isDomainOrAppNameAllowedForSite(clientType, privacyBits.isClientSideGenerated(), siteId, domainOrAppName, keys)) {
181187
return DecryptionResponse.makeError(DecryptionStatus.DOMAIN_OR_APP_NAME_CHECK_FAILED, established, siteId, siteKey.getSiteId(), identityType, advertisingTokenVersion, privacyBits.isClientSideGenerated(), expiry);
182188
}
@@ -398,6 +404,42 @@ public static byte[] decryptGCM(byte[] encryptedBytes, int offset, byte[] secret
398404
final Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
399405
c.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
400406
return c.doFinal(encryptedBytes, offset + GCM_IV_LENGTH, encryptedBytes.length - offset - GCM_IV_LENGTH);
407+
} catch (javax.crypto.AEADBadTagException e) {
408+
// Tag verification failed
409+
if (skipAeadCheck) {
410+
// Skip AEAD tag check - decrypt without tag verification using CTR mode
411+
// GCM uses CTR mode internally for encryption, so we can decrypt using CTR mode
412+
try {
413+
final SecretKey key = new SecretKeySpec(secretBytes, "AES");
414+
// Extract IV and ciphertext (excluding the tag)
415+
byte[] iv = Arrays.copyOfRange(encryptedBytes, offset, offset + GCM_IV_LENGTH);
416+
int totalLength = encryptedBytes.length - offset - GCM_IV_LENGTH;
417+
int ciphertextLength = totalLength - GCM_AUTHTAG_LENGTH;
418+
419+
if (ciphertextLength > 0) {
420+
byte[] ciphertextWithoutTag = Arrays.copyOfRange(encryptedBytes, offset + GCM_IV_LENGTH, offset + GCM_IV_LENGTH + ciphertextLength);
421+
422+
// GCM uses CTR mode internally. Create a 16-byte IV for CTR mode:
423+
// First 12 bytes are the GCM IV, last 4 bytes are counter (starting at 1 for GCM ciphertext)
424+
byte[] ctrIv = new byte[16];
425+
System.arraycopy(iv, 0, ctrIv, 0, GCM_IV_LENGTH);
426+
// Set counter to 1 (big-endian) - GCM uses counter 0 for tag, counter 1+ for ciphertext
427+
ctrIv[12] = 0;
428+
ctrIv[13] = 0;
429+
ctrIv[14] = 0;
430+
ctrIv[15] = 1;
431+
432+
// Use CTR mode which doesn't verify tags
433+
Cipher ctrCipher = Cipher.getInstance("AES/CTR/NoPadding");
434+
ctrCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ctrIv));
435+
return ctrCipher.doFinal(ciphertextWithoutTag);
436+
}
437+
} catch (Exception fallbackEx) {
438+
// If fallback fails, throw original exception
439+
throw new RuntimeException("Unable to Decrypt (tag verification failed and skip mode also failed)", e);
440+
}
441+
}
442+
throw new RuntimeException("Unable to Decrypt", e);
401443
} catch (Exception e) {
402444
throw new RuntimeException("Unable to Decrypt", e);
403445
}

0 commit comments

Comments
 (0)