Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
31cb314
added UID v4 generation functions
sophia-chen-ttd Aug 1, 2025
59d7c08
Added UID V4 unit test and fixed FLH bug
gmsdelmundo Aug 13, 2025
78dc5fc
Updated test
gmsdelmundo Aug 20, 2025
9347011
Updated raw UID2 version generation logic
gmsdelmundo Aug 21, 2025
e8740b5
Merged main
gmsdelmundo Aug 28, 2025
758ddf2
Fixed tests
gmsdelmundo Aug 28, 2025
5f5ecef
Updated identity env to pass directly into UID2 generation instead
gmsdelmundo Sep 2, 2025
30f8303
Added /v3/identity/map tests
gmsdelmundo Sep 2, 2025
4feda12
Added /v2/token/generate tests
gmsdelmundo Sep 2, 2025
6a2386a
Updated /v3/identity/map tests
gmsdelmundo Sep 2, 2025
31f57ef
Added /v2/identity/map and /v2/token/validate tests
gmsdelmundo Sep 2, 2025
8acb47a
Added /v2/token/client-generate tests
gmsdelmundo Sep 3, 2025
317ad61
Added /v2/token/refresh tests
gmsdelmundo Sep 3, 2025
7bd260c
Merged main
gmsdelmundo Sep 3, 2025
23bd981
Combined tests
gmsdelmundo Sep 3, 2025
43cecc7
Added v2/identity/map and v3/identity/map optout tests
gmsdelmundo Sep 3, 2025
ac597c8
Added v2/token/generate and v2/token/refresh optout tests
gmsdelmundo Sep 3, 2025
f00359f
Added v2/token/logout test
gmsdelmundo Sep 3, 2025
ccc7158
Merged main
gmsdelmundo Sep 3, 2025
9f1d46a
Updated formatting
gmsdelmundo Sep 3, 2025
4e3c31b
Updated identity enums
gmsdelmundo Sep 3, 2025
6613350
Updated formatting
gmsdelmundo Sep 3, 2025
c702d41
Updated mock salt files
gmsdelmundo Sep 4, 2025
ff87718
Added v2/token/generate and v2/token/client-generate refresh optout t…
gmsdelmundo Sep 4, 2025
01389b1
Added v2/optout/status tests
gmsdelmundo Sep 4, 2025
dc65abe
Merge branch 'main' into gdm-UID2-5837-v4-uid
gmsdelmundo Sep 8, 2025
a04bc24
Fixed v4 metadata and added tests
gmsdelmundo Sep 16, 2025
fea2a8f
Updated null salt check and cleaned up test code
gmsdelmundo Sep 16, 2025
ba79783
Base64-encoded last 16 bytes of FLH for v4 UID
gmsdelmundo Sep 17, 2025
15e7820
Updated v4 UID IV generation
gmsdelmundo Sep 18, 2025
fa29edd
[CI Pipeline] Released Snapshot version: 5.58.10-alpha-222-SNAPSHOT
Sep 19, 2025
14c260a
writing key id as big endian
sophia-chen-ttd Sep 22, 2025
3cd9c6e
Merge pull request #2034 from IABTechLab/sch-UID2-5854_key_id_patch
gmsdelmundo Sep 23, 2025
a396a4c
Merged main
gmsdelmundo Sep 24, 2025
dcf4124
[CI Pipeline] Released Snapshot version: 5.58.38-alpha-231-SNAPSHOT
Sep 26, 2025
254a183
Merged main
gmsdelmundo Sep 26, 2025
439045f
[CI Pipeline] Released Snapshot version: 5.58.46-alpha-233-SNAPSHOT
Sep 26, 2025
a63e1fb
Updated var naming and Cipher to ThreadLocal
gmsdelmundo Sep 30, 2025
ae99bbb
Merge branch 'gdm-UID2-5837-v4-uid' of github.com:IABTechLab/uid2-ope…
gmsdelmundo Sep 30, 2025
d99a1f0
Merged main
gmsdelmundo Sep 30, 2025
bd1c66e
Updated .trivyignore
gmsdelmundo Sep 30, 2025
97e428a
[CI Pipeline] Released Snapshot version: 5.58.51-alpha-236-SNAPSHOT
Sep 30, 2025
c953227
Merged vuln changes
gmsdelmundo Oct 2, 2025
baecb11
Merge branch 'main' into gdm-UID2-5837-v4-uid
gmsdelmundo Oct 3, 2025
be44cf4
[CI Pipeline] Released Snapshot version: 5.58.54-alpha-239-SNAPSHOT
Oct 3, 2025
8072bef
Merged main
gmsdelmundo Oct 8, 2025
aca3947
Added raw UID version counter
gmsdelmundo Oct 8, 2025
5200af5
[CI Pipeline] Released Snapshot version: 5.58.62-alpha-244-SNAPSHOT
Oct 9, 2025
bad6b68
Pre-registered raw UID version counters
gmsdelmundo Oct 9, 2025
a83a0aa
Changed null advertising ID to throw runtime exception instead
gmsdelmundo Oct 9, 2025
65eb255
Merge branch 'gdm-UID2-5837-v4-uid' of github.com:IABTechLab/uid2-ope…
gmsdelmundo Oct 9, 2025
dbb152f
Merged main
gmsdelmundo Oct 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import com.uid2.operator.vertx.ClientInputValidationException;

public enum IdentityEnvironment {
TEST(0), INTEG(1), PROD(2);
TEST(0),
INTEG(1),
PROD(2);

private final int value;

Expand Down Expand Up @@ -34,4 +36,4 @@ public static IdentityEnvironment fromString(String value) {
default -> throw new ClientInputValidationException("Invalid valid for IdentityEnvironment: " + value);
};
}
}
}
3 changes: 1 addition & 2 deletions src/main/java/com/uid2/operator/model/IdentityRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ public IdentityRequest(
PublisherIdentity publisherIdentity,
UserIdentity userIdentity,
OptoutCheckPolicy tokenGeneratePolicy,
IdentityEnvironment identityEnvironment)
{
IdentityEnvironment identityEnvironment) {
this.publisherIdentity = publisherIdentity;
this.userIdentity = userIdentity;
this.optoutCheckPolicy = tokenGeneratePolicy;
Expand Down
30 changes: 18 additions & 12 deletions src/main/java/com/uid2/operator/model/IdentityScope.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,29 @@ public enum IdentityScope {
UID2(0),
EUID(1);

public final int value;
private final int value;

IdentityScope(int value) { this.value = value; }
IdentityScope(int value) {
this.value = value;
}

public int getValue() {
return value;
}

public static IdentityScope fromValue(int value) {
switch (value) {
case 0: return UID2;
case 1: return EUID;
default: throw new ClientInputValidationException("Invalid value for IdentityScope: " + value);
}
return switch (value) {
case 0 -> UID2;
case 1 -> EUID;
default -> throw new ClientInputValidationException("Invalid value for IdentityScope: " + value);
};
}

public static IdentityScope fromString(String str) {
switch (str.toLowerCase()) {
case "uid2": return UID2;
case "euid": return EUID;
default: throw new ClientInputValidationException("Invalid string for IdentityScope: " + str);
}
return switch (str.toLowerCase()) {
case "uid2" -> UID2;
case "euid" -> EUID;
default -> throw new ClientInputValidationException("Invalid string for IdentityScope: " + str);
};
}
}
23 changes: 15 additions & 8 deletions src/main/java/com/uid2/operator/model/IdentityType.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@
import com.uid2.operator.vertx.ClientInputValidationException;

public enum IdentityType {
Email(0), Phone(1);
Email(0),
Phone(1);

public final int value;
private final int value;

IdentityType(int value) { this.value = value; }
IdentityType(int value) {
this.value = value;
}

public int getValue() {
return value;
}

public static IdentityType fromValue(int value) {
switch (value) {
case 0: return Email;
case 1: return Phone;
default: throw new ClientInputValidationException("Invalid valid for IdentityType: " + value);
}
return switch (value) {
case 0 -> Email;
case 1 -> Phone;
default -> throw new ClientInputValidationException("Invalid valid for IdentityType: " + value);
};
}
}
28 changes: 28 additions & 0 deletions src/main/java/com/uid2/operator/model/IdentityVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.uid2.operator.model;

import com.uid2.operator.vertx.ClientInputValidationException;

public enum IdentityVersion {
V2(-1), // V2 raw UIDs don't encode version
V3(0),
V4(1);

private final int value;

IdentityVersion(int value) {
this.value = value;
}

public int getValue() {
return value;
}

public static IdentityVersion fromValue(int value) {
return switch (value) {
case -1 -> V2;
case 0 -> V3;
case 1 -> V4;
default -> throw new ClientInputValidationException("Invalid valid for IdentityVersion: " + value);
};
}
}
3 changes: 1 addition & 2 deletions src/main/java/com/uid2/operator/model/KeyManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import java.util.stream.Collectors;

public class KeyManager {
private static final Logger LOGGER = LoggerFactory.getLogger(UIDOperatorVerticle.class);
private static final Logger LOGGER = LoggerFactory.getLogger(KeyManager.class);
private final IKeysetKeyStore keysetKeyStore;
private final RotatingKeysetProvider keysetProvider;

Expand Down Expand Up @@ -76,7 +76,6 @@ public KeysetKey getKey(int keyId) {
return this.keysetKeyStore.getSnapshot().getKey(keyId);
}


public List<KeysetKey> getKeysForSharingOrDsps() {
Map<Integer, Keyset> keysetMap = this.keysetProvider.getSnapshot().getAllKeysets();
List<KeysetKey> keys = keysetKeyStore.getSnapshot().getAllKeysetKeys();
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/com/uid2/operator/model/MapRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ public MapRequest(
UserIdentity userIdentity,
OptoutCheckPolicy optoutCheckPolicy,
Instant asOf,
IdentityEnvironment identityEnvironment)
{
IdentityEnvironment identityEnvironment) {
this.userIdentity = userIdentity;
this.optoutCheckPolicy = optoutCheckPolicy;
this.asOf = asOf;
Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/uid2/operator/model/UserIdentity.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.time.Instant;
import java.util.Arrays;
import java.util.Objects;

public class UserIdentity {
public final IdentityScope identityScope;
Expand Down
64 changes: 25 additions & 39 deletions src/main/java/com/uid2/operator/service/EncodingUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import java.util.Base64;
import java.util.UUID;

public class EncodingUtils {
public final class EncodingUtils {
private EncodingUtils() {
}

public static String toBase64String(byte[] b) {
return Base64.getEncoder().encodeToString(b);
Expand All @@ -18,9 +20,13 @@ public static byte[] toBase64(byte[] b) {
return Base64.getEncoder().encode(b);
}

public static byte[] fromBase64(String s) { return Base64.getDecoder().decode(s); }
public static byte[] fromBase64(String s) {
return Base64.getDecoder().decode(s);
}

public static byte[] fromBase64(byte[] b) { return Base64.getDecoder().decode(b); }
public static byte[] fromBase64(byte[] b) {
return Base64.getDecoder().decode(b);
}

public static String getSha256(String input, String salt) {
return toBase64String(getSha256Bytes(input, salt));
Expand All @@ -34,10 +40,18 @@ public static byte[] getSha256Bytes(String input) {
return getSha256Bytes(input, null);
}

public static byte[] getSha256Bytes(byte[] input) {
return getSha256Bytes(input, null);
}

public static byte[] getSha256Bytes(String input, String salt) {
return getSha256Bytes(input.getBytes(), salt);
}

public static byte[] getSha256Bytes(byte[] input, String salt) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(input.getBytes());
md.update(input);
if (salt != null) {
md.update(salt.getBytes());
}
Expand All @@ -47,34 +61,6 @@ public static byte[] getSha256Bytes(String input, String salt) {
}
}

public static String generateIdGuid(String value) {
byte[] b = value.getBytes(Charset.forName("UTF8"));
long high = 0L;
high = high | (long) b[0] << (7 * 8);
high = high | (long) b[1] << (6 * 8);
high = high | (long) b[2] << (5 * 8);
high = high | (long) b[3] << (5 * 8);
high = high | (long) b[4] << (3 * 8);
high = high | (long) b[5] << (2 * 8);
high = high | (long) b[6] << (1 * 8);
high = high | (long) b[7];

long low = 0;
low = low | (long) b[8] << (7 * 8);
low = low | (long) b[9] << (6 * 8);
low = low | (long) b[10] << (5 * 8);
low = low | (long) b[11] << (4 * 8);
low = low | (long) b[12] << (3 * 8);
low = low | (long) b[13] << (2 * 8);
low = low | (long) b[14] << (1 * 8);
low = low | (long) b[15];

String uid = new UUID(high, low).toString();

return uid;

}

public static Instant NowUTCMillis() {
return Instant.now().truncatedTo(ChronoUnit.MILLIS);
}
Expand All @@ -84,21 +70,21 @@ public static Instant NowUTCMillis(Clock clock) {
}

public static byte[] fromHexString(String hs) throws NumberFormatException {
if(hs.length() % 2 == 1) {
if (hs.length() % 2 == 1) {
throw new NumberFormatException("input " + hs.substring(0, 5) + "... is not a valid hex string - odd length");
}

byte[] s = new byte[hs.length() / 2];
for(int i = 0; i < hs.length(); i++) {
for (int i = 0; i < hs.length(); i++) {
int v; char c = hs.charAt(i);
if(c >= '0' && c <= '9') v = c - '0';
else if(c >= 'A' && c <= 'F') v = c - 'A' + 10;
else if(c >= 'a' && c <= 'f') v = c - 'a' + 10;
if (c >= '0' && c <= '9') v = c - '0';
else if (c >= 'A' && c <= 'F') v = c - 'A' + 10;
else if (c >= 'a' && c <= 'f') v = c - 'a' + 10;
else throw new NumberFormatException("input " + hs.substring(0, 5) + "... is not a valid hex string - invalid character");
if (i % 2 == 0) {
s[i / 2] = (byte) (s[i / 2] | (byte)((v << 4) & 0xFF));
s[i / 2] = (byte) (s[i / 2] | (byte) ((v << 4) & 0xFF));
} else {
s[i / 2] = (byte) (s[i / 2] | (byte)(v & 0xFF));
s[i / 2] = (byte) (s[i / 2] | (byte) (v & 0xFF));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ public AdvertisingToken decodeAdvertisingTokenV2(Buffer b) {
} catch (Exception e) {
throw new RuntimeException("Couldn't decode advertisingTokenV2", e);
}

}

public AdvertisingToken decodeAdvertisingTokenV3orV4(Buffer b, byte[] bytes, TokenVersion tokenVersion) {
Expand All @@ -253,8 +252,7 @@ public AdvertisingToken decodeAdvertisingTokenV3orV4(Buffer b, byte[] bytes, Tok
final IdentityScope identityScope = id.length == 32 ? IdentityScope.UID2 : decodeIdentityScopeV3(id[0]);
final IdentityType identityType = id.length == 32 ? IdentityType.Email : decodeIdentityTypeV3(id[0]);

if (id.length > 32)
{
if (id.length > 32) {
if (identityScope != decodeIdentityScopeV3(b.getByte(0))) {
throw new ClientInputValidationException("Failed decoding advertisingTokenV3: Identity scope mismatch");
}
Expand Down Expand Up @@ -338,7 +336,6 @@ public static String bytesToBase64Token(byte[] advertisingTokenBytes, TokenVersi

@Override
public IdentityTokens encode(AdvertisingToken advertisingToken, RefreshToken refreshToken, Instant refreshFrom, Instant asOf) {

final byte[] advertisingTokenBytes = encode(advertisingToken, asOf);
final String base64AdvertisingToken = bytesToBase64Token(advertisingTokenBytes, advertisingToken.version);

Expand Down Expand Up @@ -367,16 +364,16 @@ private byte[] encryptIdentityV2(PublisherIdentity publisherIdentity, UserIdenti
}
}

static private byte encodeIdentityTypeV3(UserIdentity userIdentity) {
return (byte) (TokenUtils.encodeIdentityScope(userIdentity.identityScope) | (userIdentity.identityType.value << 2) | 3);
private static byte encodeIdentityTypeV3(UserIdentity userIdentity) {
return (byte) (TokenUtils.encodeIdentityScope(userIdentity.identityScope) | (userIdentity.identityType.getValue() << 2) | 3);
// "| 3" is used so that the 2nd char matches the version when V3 or higher. Eg "3" for V3 and "4" for V4
}

static private IdentityScope decodeIdentityScopeV3(byte value) {
private static IdentityScope decodeIdentityScopeV3(byte value) {
return IdentityScope.fromValue((value & 0x10) >> 4);
}

static private IdentityType decodeIdentityTypeV3(byte value) {
private static IdentityType decodeIdentityTypeV3(byte value) {
return IdentityType.fromValue((value & 0xf) >> 2);
}

Expand All @@ -392,7 +389,7 @@ static PublisherIdentity decodePublisherIdentityV3(Buffer b, int offset) {

static void encodeOperatorIdentityV3(Buffer b, OperatorIdentity operatorIdentity) {
b.appendInt(operatorIdentity.siteId);
b.appendByte((byte)operatorIdentity.operatorType.value);
b.appendByte((byte) operatorIdentity.operatorType.value);
b.appendInt(operatorIdentity.operatorVersion);
b.appendInt(operatorIdentity.operatorKeyId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.util.List;

public interface IUIDOperatorService {

IdentityTokens generateIdentity(IdentityRequest request, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter);

RefreshResponse refreshIdentity(RefreshToken token, Duration refreshIdentityAfter, Duration refreshExpiresAfter, Duration identityExpiresAfter, IdentityEnvironment env);
Expand All @@ -25,6 +24,4 @@ public interface IUIDOperatorService {
void invalidateTokensAsync(UserIdentity userIdentity, Instant asOf, String uidTraceId, IdentityEnvironment env, Handler<AsyncResult<Instant>> handler);

boolean advertisingTokenMatches(String advertisingToken, UserIdentity userIdentity, Instant asOf, IdentityEnvironment env);

Instant getLatestOptoutEntry(UserIdentity userIdentity, Instant asOf);
}
Loading