Skip to content

Commit

Permalink
1. Renamed IdentityType to DiiType
Browse files Browse the repository at this point in the history
2. In InputUtil, renamed Identity* member variable/methods to Dii* to indicate it's Dii
3. Change TokenUtils class getIdentityHash to getDiiHash and same for getDiiHashString
  • Loading branch information
sunnywu committed Dec 9, 2024
1 parent 6bb1fb9 commit a8f0915
Show file tree
Hide file tree
Showing 20 changed files with 293 additions and 298 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.uid2.operator.model;
package com.uid2.operator.model.identities;

import com.uid2.operator.vertx.ClientInputValidationException;

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

public final int value;

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

public static IdentityType fromValue(int value) {
public static DiiType fromValue(int value) {
switch (value) {
case 0: return Email;
case 1: return Phone;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
package com.uid2.operator.model.identities;

import com.uid2.operator.model.IdentityScope;
import com.uid2.operator.model.IdentityType;

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

/**
* Contains a first level salted hash computed from Hashed DII (email/phone number)
* @param establishedAt for brand new token generation, it should be the time it is generated if the first level hash is from token/refresh call, it will be when the raw UID was originally created in the earliest token generation
*/
public record FirstLevelHash(IdentityScope identityScope, IdentityType identityType, byte[] firstLevelHash,
public record FirstLevelHash(IdentityScope identityScope, DiiType diiType, byte[] firstLevelHash,
Instant establishedAt) {

// explicitly not checking establishedAt - this is only for making sure the first level hash matches a new input
public boolean matches(FirstLevelHash that) {
return this.identityScope.equals(that.identityScope) &&
this.identityType.equals(that.identityType) &&
this.diiType.equals(that.diiType) &&
Arrays.equals(this.firstLevelHash, that.firstLevelHash);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.uid2.operator.model.identities;

import com.uid2.operator.model.IdentityScope;
import com.uid2.operator.model.IdentityType;

// Contains a hash DII,
// Contains a hash Directly Identifying Information (DII) (email or phone) see https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii
// This hash can either be computed from a raw email/phone number DII input or provided by the UID Participant directly
public record HashedDii(IdentityScope identityScope, IdentityType identityType, byte[] hashedDii) {
//
public record HashedDii(IdentityScope identityScope, DiiType diiType, byte[] hashedDii) {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.uid2.operator;
package com.uid2.operator.model.identities;

import com.uid2.operator.service.EncodingUtils;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.uid2.operator.model;
package com.uid2.operator.model.identities;

import com.uid2.operator.vertx.ClientInputValidationException;

Expand Down
7 changes: 2 additions & 5 deletions src/main/java/com/uid2/operator/model/identities/RawUid.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package com.uid2.operator.model.identities;

import com.uid2.operator.model.IdentityScope;
import com.uid2.operator.model.IdentityType;

import java.util.Arrays;

// A raw UID is stored inside
public record RawUid(IdentityScope identityScope, IdentityType identityType, byte[] rawUid) {
public record RawUid(IdentityScope identityScope, DiiType diiType, byte[] rawUid) {

public boolean matches(RawUid that) {
return this.identityScope.equals(that.identityScope) &&
this.identityType.equals(that.identityType) &&
this.diiType.equals(that.diiType) &&
Arrays.equals(this.rawUid, that.rawUid);
}
}
32 changes: 17 additions & 15 deletions src/main/java/com/uid2/operator/service/EncryptedTokenEncoder.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.uid2.operator.service;

import com.uid2.operator.model.*;
import com.uid2.operator.model.identities.DiiType;
import com.uid2.operator.model.identities.FirstLevelHash;
import com.uid2.operator.model.identities.IdentityScope;
import com.uid2.operator.model.identities.RawUid;
import com.uid2.operator.util.PrivacyBits;
import com.uid2.operator.vertx.ClientInputValidationException;
Expand Down Expand Up @@ -69,7 +71,7 @@ private byte[] encodeIntoAdvertisingTokenV3(AdvertisingTokenRequest t, KeysetKey
masterPayload.appendBytes(AesGcm.encrypt(sitePayload.getBytes(), siteKey).getPayload());

final Buffer b = Buffer.buffer(164);
b.appendByte(encodeIdentityTypeV3(t.rawUid.identityScope(), t.rawUid.identityType()));
b.appendByte(encodeIdentityTypeV3(t.rawUid.identityScope(), t.rawUid.diiType()));
b.appendByte((byte) t.version.rawVersion);
b.appendInt(masterKey.getId());
b.appendBytes(AesGcm.encrypt(masterPayload.getBytes(), masterKey).getPayload());
Expand Down Expand Up @@ -129,7 +131,7 @@ private TokenRefreshRequest decodeRefreshTokenV2(Buffer b) {
TokenVersion.V2, createdAt, validTill,
new OperatorIdentity(0, OperatorType.Service, 0, 0),
new SourcePublisher(siteId),
new FirstLevelHash(IdentityScope.UID2, IdentityType.Email, identity,
new FirstLevelHash(IdentityScope.UID2, DiiType.Email, identity,
Instant.ofEpochMilli(establishedMillis)),
privacyBits);
}
Expand All @@ -152,19 +154,19 @@ private TokenRefreshRequest decodeRefreshTokenV3(Buffer b, byte[] bytes) {
final PrivacyBits privacyBits = PrivacyBits.fromInt(b2.getInt(45));
final Instant establishedAt = Instant.ofEpochMilli(b2.getLong(49));
final IdentityScope identityScope = decodeIdentityScopeV3(b2.getByte(57));
final IdentityType identityType = decodeIdentityTypeV3(b2.getByte(57));
final DiiType diiType = decodeIdentityTypeV3(b2.getByte(57));
final byte[] firstLevelHash = b2.getBytes(58, 90);

if (identityScope != decodeIdentityScopeV3(b.getByte(0))) {
throw new ClientInputValidationException("Failed to decode refreshTokenV3: Identity scope mismatch");
}
if (identityType != decodeIdentityTypeV3(b.getByte(0))) {
if (diiType != decodeIdentityTypeV3(b.getByte(0))) {
throw new ClientInputValidationException("Failed to decode refreshTokenV3: Identity type mismatch");
}

return new TokenRefreshRequest(
TokenVersion.V3, createdAt, expiresAt, operatorIdentity, sourcePublisher,
new FirstLevelHash(identityScope, identityType, firstLevelHash, establishedAt),
new FirstLevelHash(identityScope, diiType, firstLevelHash, establishedAt),
privacyBits);
}

Expand Down Expand Up @@ -233,7 +235,7 @@ public AdvertisingTokenRequest decodeAdvertisingTokenV2(Buffer b) {
Instant.ofEpochMilli(expiresMillis),
new OperatorIdentity(0, OperatorType.Service, 0, masterKeyId),
new SourcePublisher(siteId, siteKeyId, 0),
new RawUid(IdentityScope.UID2, IdentityType.Email, rawUid),
new RawUid(IdentityScope.UID2, DiiType.Email, rawUid),
privacyBits,
Instant.ofEpochMilli(establishedMillis)
);
Expand Down Expand Up @@ -262,21 +264,21 @@ public AdvertisingTokenRequest decodeAdvertisingTokenV3orV4(Buffer b, byte[] byt
final Instant refreshedAt = Instant.ofEpochMilli(sitePayload.getLong(28));
final byte[] rawUid = sitePayload.slice(36, sitePayload.length()).getBytes();
final IdentityScope identityScope = rawUid.length == 32 ? IdentityScope.UID2 : decodeIdentityScopeV3(rawUid[0]);
final IdentityType identityType = rawUid.length == 32 ? IdentityType.Email : decodeIdentityTypeV3(rawUid[0]);
final DiiType diiType = rawUid.length == 32 ? DiiType.Email : decodeIdentityTypeV3(rawUid[0]);

if (rawUid.length > 32)
{
if (identityScope != decodeIdentityScopeV3(b.getByte(0))) {
throw new ClientInputValidationException("Failed decoding advertisingTokenV3: Identity scope mismatch");
}
if (identityType != decodeIdentityTypeV3(b.getByte(0))) {
if (diiType != decodeIdentityTypeV3(b.getByte(0))) {
throw new ClientInputValidationException("Failed decoding advertisingTokenV3: Identity type mismatch");
}
}

return new AdvertisingTokenRequest(
tokenVersion, createdAt, expiresAt, operatorIdentity, sourcePublisher,
new RawUid(identityScope, identityType, rawUid),
new RawUid(identityScope, diiType, rawUid),
privacyBits, establishedAt
);
}
Expand Down Expand Up @@ -326,11 +328,11 @@ public byte[] encodeIntoRefreshTokenV3(TokenRefreshRequest t, KeysetKey serviceK
encodePublisherRequesterV3(refreshPayload, t.sourcePublisher);
refreshPayload.appendInt(t.privacyBits.getAsInt());
refreshPayload.appendLong(t.firstLevelHash.establishedAt().toEpochMilli());
refreshPayload.appendByte(encodeIdentityTypeV3(t.firstLevelHash.identityScope(), t.firstLevelHash.identityType()));
refreshPayload.appendByte(encodeIdentityTypeV3(t.firstLevelHash.identityScope(), t.firstLevelHash.diiType()));
refreshPayload.appendBytes(t.firstLevelHash.firstLevelHash());

final Buffer b = Buffer.buffer(124);
b.appendByte(encodeIdentityTypeV3(t.firstLevelHash.identityScope(), t.firstLevelHash.identityType()));
b.appendByte(encodeIdentityTypeV3(t.firstLevelHash.identityScope(), t.firstLevelHash.diiType()));
b.appendByte((byte) t.version.rawVersion);
b.appendInt(serviceKey.getId());
b.appendBytes(AesGcm.encrypt(refreshPayload.getBytes(), serviceKey).getPayload());
Expand Down Expand Up @@ -402,17 +404,17 @@ private byte[] encryptIdentityV2(SourcePublisher sourcePublisher, byte[] id, Pri
}
}

static private byte encodeIdentityTypeV3(IdentityScope identityScope, IdentityType identityType) {
return (byte) (TokenUtils.encodeIdentityScope(identityScope) | (identityType.value << 2) | 3);
static private byte encodeIdentityTypeV3(IdentityScope identityScope, DiiType diiType) {
return (byte) (TokenUtils.encodeIdentityScope(identityScope) | (diiType.value << 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) {
return IdentityScope.fromValue((value & 0x10) >> 4);
}

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

static void encodePublisherRequesterV3(Buffer b, SourcePublisher sourcePublisher) {
Expand Down
57 changes: 29 additions & 28 deletions src/main/java/com/uid2/operator/service/InputUtil.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.uid2.operator.service;

import com.uid2.operator.model.IdentityScope;
import com.uid2.operator.model.IdentityType;
import com.uid2.operator.model.identities.IdentityScope;
import com.uid2.operator.model.identities.DiiType;
import com.uid2.operator.model.identities.HashedDii;

public class InputUtil {
Expand Down Expand Up @@ -167,7 +167,7 @@ public static String normalizeEmailString(String email) {
return addressPartToUse.append('@').append(domainPart).toString();
}

public enum IdentityInputType {
public enum DiiInputType {
Raw,
Hash
}
Expand All @@ -183,62 +183,63 @@ private static enum EmailParsingState {
public static class InputVal {
private final String provided;
private final String normalized;
private final IdentityType identityType;
private final IdentityInputType inputType;
//Directly Identifying Information (DII) (email or phone) see https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii
private final DiiType diiType;
private final DiiInputType inputType;
private final boolean valid;
private final byte[] identityInput;
private final byte[] diiInput;

public InputVal(String provided, String normalized, IdentityType identityType, IdentityInputType inputType, boolean valid) {
public InputVal(String provided, String normalized, DiiType diiType, DiiInputType inputType, boolean valid) {
this.provided = provided;
this.normalized = normalized;
this.identityType = identityType;
this.diiType = diiType;
this.inputType = inputType;
this.valid = valid;
if (valid) {
if (this.inputType == IdentityInputType.Raw) {
this.identityInput = TokenUtils.getIdentityHash(this.normalized);
if (this.inputType == DiiInputType.Raw) {
this.diiInput = TokenUtils.getDiiHash(this.normalized);
} else {
this.identityInput = EncodingUtils.fromBase64(this.normalized);
this.diiInput = EncodingUtils.fromBase64(this.normalized);
}
} else {
this.identityInput = null;
this.diiInput = null;
}
}

public static InputVal validEmail(String input, String normalized) {
return new InputVal(input, normalized, IdentityType.Email, IdentityInputType.Raw, true);
return new InputVal(input, normalized, DiiType.Email, DiiInputType.Raw, true);
}

public static InputVal invalidEmail(String input) {
return new InputVal(input, null, IdentityType.Email, IdentityInputType.Raw, false);
return new InputVal(input, null, DiiType.Email, DiiInputType.Raw, false);
}

public static InputVal validEmailHash(String input, String normalized) {
return new InputVal(input, normalized, IdentityType.Email, IdentityInputType.Hash, true);
return new InputVal(input, normalized, DiiType.Email, DiiInputType.Hash, true);
}

public static InputVal invalidEmailHash(String input) {
return new InputVal(input, null, IdentityType.Email, IdentityInputType.Hash, false);
return new InputVal(input, null, DiiType.Email, DiiInputType.Hash, false);
}

public static InputVal validPhone(String input, String normalized) {
return new InputVal(input, normalized, IdentityType.Phone, IdentityInputType.Raw, true);
return new InputVal(input, normalized, DiiType.Phone, DiiInputType.Raw, true);
}

public static InputVal invalidPhone(String input) {
return new InputVal(input, null, IdentityType.Phone, IdentityInputType.Raw, false);
return new InputVal(input, null, DiiType.Phone, DiiInputType.Raw, false);
}

public static InputVal validPhoneHash(String input, String normalized) {
return new InputVal(input, normalized, IdentityType.Phone, IdentityInputType.Hash, true);
return new InputVal(input, normalized, DiiType.Phone, DiiInputType.Hash, true);
}

public static InputVal invalidPhoneHash(String input) {
return new InputVal(input, null, IdentityType.Phone, IdentityInputType.Hash, false);
return new InputVal(input, null, DiiType.Phone, DiiInputType.Hash, false);
}

public byte[] getIdentityInput() {
return this.identityInput;
public byte[] getHashedDiiInput() {
return this.diiInput;
}

public String getProvided() {
Expand All @@ -249,21 +250,21 @@ public String getNormalized() {
return normalized;
}

public IdentityType getIdentityType() {
return identityType;
public DiiType getDiiType() {
return diiType;
}

public IdentityInputType getInputType() { return inputType; }
public DiiInputType getInputType() { return inputType; }

public boolean isValid() {
return valid;
}

public HashedDii toHashedDiiIdentity(IdentityScope identityScope) {
public HashedDii toHashedDii(IdentityScope identityScope) {
return new HashedDii(
identityScope,
this.identityType,
getIdentityInput());
this.diiType,
getHashedDiiInput());
}
}

Expand Down
Loading

0 comments on commit a8f0915

Please sign in to comment.