Skip to content

Commit 0f6463b

Browse files
committed
Merge branch 'gdm-UID2-1526-add-cstg-to-decryptionresponse' of github.com:IABTechLab/uid2-client-net into gdm-UID2-1526-add-cstg-to-decryptionresponse
2 parents b7ca4c5 + efd6a00 commit 0f6463b

File tree

6 files changed

+91
-27
lines changed

6 files changed

+91
-27
lines changed

src/UID2.Client/IUID2Client.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,18 @@ public interface IUID2Client
3838
[Obsolete("Please use Decrypt(string token) instead.")]
3939
DecryptionResponse Decrypt(string token, DateTime utcNow);
4040
DecryptionResponse Decrypt(string token);
41-
DecryptionResponse Decrypt(string token, string expectedDomainName);
41+
/// <summary>
42+
/// Decrypt advertising token to extract UID2 details and does a domain name check with the provided domainNameFromBidRequest param
43+
/// for tokens from Client Side Token Generation
44+
/// </summary>
45+
/// <param name="token">The UID2 Token </param>
46+
/// <param name="domainNameFromBidRequest">The domain name from bid request which should match the domain name of the publisher (registered with UID2 admin)
47+
/// generating this token previously using Client Side Token Generation
48+
/// </param>
49+
/// <returns>Response showing if decryption is successful and the resulting UID if successful.
50+
/// Or it could return error codes/string indicating what went wrong (such as DecryptionStatus.DomainNameCheckFailed)
51+
/// </returns>
52+
DecryptionResponse Decrypt(string token, string domainNameFromBidRequest);
4253

4354
EncryptionDataResponse Encrypt(string rawUid);
4455
[Obsolete("Please use Encrypt(string rawUid) instead.")]

src/UID2.Client/UID2Client.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,20 @@ public UID2Client(string endpoint, string authKey, string secretKey, IdentitySco
3434

3535
public DecryptionResponse Decrypt(string token)
3636
{
37-
return Decrypt(token, DateTime.UtcNow, expectedDomainName: null);
37+
return Decrypt(token, DateTime.UtcNow, null, false);
3838
}
3939

4040
public DecryptionResponse Decrypt(string token, DateTime utcNow)
4141
{
42-
return Decrypt(token, utcNow, expectedDomainName: null);
42+
return Decrypt(token, utcNow, null, false);
4343
}
4444

45-
public DecryptionResponse Decrypt(string token, string expectedDomainName)
45+
public DecryptionResponse Decrypt(string token, string domainNameFromBidRequest)
4646
{
47-
return Decrypt(token, DateTime.UtcNow, expectedDomainName);
47+
return Decrypt(token, DateTime.UtcNow, domainNameFromBidRequest, true);
4848
}
4949

50-
public DecryptionResponse Decrypt(string token, DateTime now, string expectedDomainName)
50+
private DecryptionResponse Decrypt(string token, DateTime now, string domainNameFromBidRequest, bool enableDomainNameCheck)
5151
{
5252
var container = Volatile.Read(ref _container);
5353
if (container == null)
@@ -62,7 +62,7 @@ public DecryptionResponse Decrypt(string token, DateTime now, string expectedDom
6262

6363
try
6464
{
65-
return UID2Encryption.Decrypt(token, container, now, expectedDomainName, _identityScope);
65+
return UID2Encryption.Decrypt(token, container, now, domainNameFromBidRequest, _identityScope, enableDomainNameCheck);
6666
}
6767
catch (Exception)
6868
{

src/UID2.Client/UID2Encryption.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal static class UID2Encryption
1515
public const int GCM_IV_LENGTH = 12;
1616
private static char[] BASE64_URL_SPECIAL_CHARS = { '-', '_' };
1717

18-
internal static DecryptionResponse Decrypt(string token, KeyContainer keys, DateTime now, string domainName, IdentityScope identityScope)
18+
internal static DecryptionResponse Decrypt(string token, KeyContainer keys, DateTime now, string domainName, IdentityScope identityScope, bool enableDomainNameCheck)
1919
{
2020
if (token.Length < 4)
2121
{
@@ -28,24 +28,24 @@ internal static DecryptionResponse Decrypt(string token, KeyContainer keys, Date
2828

2929
if (data[0] == 2)
3030
{
31-
return DecryptV2(Convert.FromBase64String(token), keys, now, domainName);
31+
return DecryptV2(Convert.FromBase64String(token), keys, now, domainName, enableDomainNameCheck);
3232
}
3333

3434
if (data[1] == (int)AdvertisingTokenVersion.V3)
3535
{
36-
return DecryptV3(Convert.FromBase64String(token), keys, now, identityScope, 3, domainName);
36+
return DecryptV3(Convert.FromBase64String(token), keys, now, identityScope, 3, domainName, enableDomainNameCheck);
3737
}
3838

3939
if (data[1] == (int)AdvertisingTokenVersion.V4)
4040
{
4141
//same as V3 but use Base64URL encoding
42-
return DecryptV3(UID2Base64UrlCoder.Decode(token), keys, now, identityScope, 4, domainName);
42+
return DecryptV3(UID2Base64UrlCoder.Decode(token), keys, now, identityScope, 4, domainName, enableDomainNameCheck);
4343
}
4444

4545
return DecryptionResponse.MakeError(DecryptionStatus.VersionNotSupported);
4646
}
4747

48-
private static DecryptionResponse DecryptV2(byte[] encryptedId, KeyContainer keys, DateTime now, string domainName)
48+
private static DecryptionResponse DecryptV2(byte[] encryptedId, KeyContainer keys, DateTime now, string domainName, bool enableDomainNameCheck)
4949
{
5050
var reader = new BigEndianByteReader(new MemoryStream(encryptedId));
5151

@@ -101,15 +101,15 @@ private static DecryptionResponse DecryptV2(byte[] encryptedId, KeyContainer key
101101
return new DecryptionResponse(DecryptionStatus.UserOptedOut, null, established, siteId, siteKey.SiteId, null, 2, privacyBits.IsClientSideGenerated);
102102
}
103103

104-
if (!IsDomainNameAllowedForSite(privacyBits, siteId, domainName, keys))
104+
if (enableDomainNameCheck && !IsDomainNameAllowedForSite(privacyBits, siteId, domainName, keys))
105105
{
106106
return new DecryptionResponse(DecryptionStatus.DomainNameCheckFailed, null, established, siteId, siteKey.SiteId, null, 2, privacyBits.IsClientSideGenerated);
107107
}
108108

109109
return new DecryptionResponse(DecryptionStatus.Success, idString, established, siteId, siteKey.SiteId, null, 2, privacyBits.IsClientSideGenerated);
110110
}
111111

112-
private static DecryptionResponse DecryptV3(byte[] encryptedId, KeyContainer keys, DateTime now, IdentityScope identityScope, int advertisingTokenVersion, string domainName)
112+
private static DecryptionResponse DecryptV3(byte[] encryptedId, KeyContainer keys, DateTime now, IdentityScope identityScope, int advertisingTokenVersion, string domainName, bool enableDomainNameCheck)
113113
{
114114
IdentityType identityType = GetIdentityType(encryptedId);
115115

@@ -178,7 +178,7 @@ private static DecryptionResponse DecryptV3(byte[] encryptedId, KeyContainer key
178178
return new DecryptionResponse(DecryptionStatus.UserOptedOut, null, established, siteId, siteKey.SiteId, identityType, advertisingTokenVersion, privacyBits.IsClientSideGenerated);
179179
}
180180

181-
if (!IsDomainNameAllowedForSite(privacyBits, siteId, domainName, keys))
181+
if (enableDomainNameCheck && !IsDomainNameAllowedForSite(privacyBits, siteId, domainName, keys))
182182
{
183183
return new DecryptionResponse(DecryptionStatus.DomainNameCheckFailed, null, established, siteId, siteKey.SiteId, identityType, advertisingTokenVersion,
184184
privacyBits.IsClientSideGenerated);
@@ -270,9 +270,8 @@ internal static EncryptionDataResponse EncryptData(EncryptionDataRequest request
270270
{
271271
try
272272
{
273-
// Decryption will fail if the token is a CSTG-derived token.
274-
// In that case the caller would have to provide siteId as part of the EncryptionDataRequest.
275-
DecryptionResponse decryptedToken = Decrypt(request.AdvertisingToken, keys, now, domainName: null, identityScope);
273+
// if the enableDomainNameCheck param is enabled , the caller would have to provide siteId as part of the EncryptionDataRequest.
274+
DecryptionResponse decryptedToken = Decrypt(request.AdvertisingToken, keys, now, domainName: null, identityScope, false);
276275
if (!decryptedToken.Success)
277276
{
278277
return EncryptionDataResponse.MakeError(EncryptionStatus.TokenDecryptFailure);

test/UID2.Client.Test/EncryptionTestsV2.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public void TokenIsCstgDerivedTest(string domainName)
4747
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
4848
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build();
4949
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
50-
var res = _client.Decrypt(advertisingToken, NOW, domainName);
50+
var res = _client.Decrypt(advertisingToken, domainName);
5151
Assert.True(res.IsClientSideGenerated);
5252
Assert.True(res.Success);
5353
Assert.Equal(DecryptionStatus.Success, res.Status);
@@ -67,12 +67,30 @@ public void TokenIsCstgDerivedDomainNameFailTest(string domainName)
6767
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
6868
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build();
6969
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
70-
var res = _client.Decrypt(advertisingToken, NOW, domainName);
70+
var res = _client.Decrypt(advertisingToken, domainName);
7171
Assert.True(res.IsClientSideGenerated);
7272
Assert.False(res.Success);
7373
Assert.Equal(DecryptionStatus.DomainNameCheckFailed, res.Status);
7474
Assert.Null(res.Uid);
7575
}
76+
77+
// if there is domain name associated with sites but we explicitly call
78+
// DecryptionResponse Decrypt(string token) or DecryptionResponse Decrypt(string token, DateTime utcNow)
79+
// and we do not want to do domain name check
80+
// the Decrypt function would still decrypt successfully
81+
// in case DSP does not want to enable domain name check
82+
[Fact]
83+
public void TokenIsCstgDerivedNoDomainNameTest()
84+
{
85+
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
86+
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build();
87+
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
88+
var res = _client.Decrypt(advertisingToken);
89+
Assert.True(res.IsClientSideGenerated);
90+
Assert.True(res.Success);
91+
Assert.Equal(DecryptionStatus.Success, res.Status);
92+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
93+
}
7694

7795
[Theory]
7896
// Any domain name is OK, because the token is not client-side generated.
@@ -85,7 +103,7 @@ public void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainName)
85103
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
86104
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(false).Build();
87105
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
88-
var res = _client.Decrypt(advertisingToken, NOW, domainName);
106+
var res = _client.Decrypt(advertisingToken, domainName);
89107
Assert.False(res.IsClientSideGenerated);
90108
Assert.True(res.Success);
91109
Assert.Equal(DecryptionStatus.Success, res.Status);

test/UID2.Client.Test/EncryptionTestsV3.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public void TokenIsCstgDerivedTest(string domainName)
6767
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
6868
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build();
6969
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
70-
var res = _client.Decrypt(advertisingToken, NOW, domainName);
70+
var res = _client.Decrypt(advertisingToken, domainName);
7171
Assert.True(res.IsClientSideGenerated);
7272
Assert.True(res.Success);
7373
Assert.Equal(DecryptionStatus.Success, res.Status);
@@ -87,12 +87,30 @@ public void TokenIsCstgDerivedDomainNameFailTest(string domainName)
8787
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
8888
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build();
8989
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
90-
var res = _client.Decrypt(advertisingToken, NOW, domainName);
90+
var res = _client.Decrypt(advertisingToken, domainName);
9191
Assert.True(res.IsClientSideGenerated);
9292
Assert.False(res.Success);
9393
Assert.Equal(DecryptionStatus.DomainNameCheckFailed, res.Status);
9494
Assert.Null(res.Uid);
9595
}
96+
97+
// if there is domain name associated with sites but we explicitly call
98+
// DecryptionResponse Decrypt(string token) or DecryptionResponse Decrypt(string token, DateTime utcNow)
99+
// and we do not want to do domain name check
100+
// the Decrypt function would still decrypt successfully
101+
// in case DSP does not want to enable domain name check
102+
[Fact]
103+
public void TokenIsCstgDerivedNoDomainNameTest()
104+
{
105+
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
106+
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build();
107+
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
108+
var res = _client.Decrypt(advertisingToken);
109+
Assert.True(res.IsClientSideGenerated);
110+
Assert.True(res.Success);
111+
Assert.Equal(DecryptionStatus.Success, res.Status);
112+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
113+
}
96114

97115
[Theory]
98116
// Any domain name is OK, because the token is not client-side generated.
@@ -105,7 +123,7 @@ public void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainName)
105123
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
106124
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(false).Build();
107125
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
108-
var res = _client.Decrypt(advertisingToken, NOW, domainName);
126+
var res = _client.Decrypt(advertisingToken, domainName);
109127
Assert.False(res.IsClientSideGenerated);
110128
Assert.True(res.Success);
111129
Assert.Equal(DecryptionStatus.Success, res.Status);

test/UID2.Client.Test/EncryptionTestsV4.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public void TokenIsCstgDerivedTest(string domainName)
185185
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build();
186186
string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
187187
ValidateAdvertisingToken(advertisingToken, IdentityScope.UID2, IdentityType.Email);
188-
var res = _client.Decrypt(advertisingToken, NOW, domainName);
188+
var res = _client.Decrypt(advertisingToken, domainName);
189189
Assert.True(res.IsClientSideGenerated);
190190
Assert.True(res.Success);
191191
Assert.Equal(DecryptionStatus.Success, res.Status);
@@ -205,12 +205,30 @@ public void TokenIsCstgDerivedDomainNameFailTest(string domainName)
205205
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
206206
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build();
207207
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
208-
var res = _client.Decrypt(advertisingToken, NOW, domainName);
208+
var res = _client.Decrypt(advertisingToken, domainName);
209209
Assert.True(res.IsClientSideGenerated);
210210
Assert.False(res.Success);
211211
Assert.Equal(DecryptionStatus.DomainNameCheckFailed, res.Status);
212212
Assert.Null(res.Uid);
213213
}
214+
215+
// if there is domain name associated with sites but we explicitly call
216+
// DecryptionResponse Decrypt(string token) or DecryptionResponse Decrypt(string token, DateTime utcNow)
217+
// and we do not want to do domain name check
218+
// the Decrypt function would still decrypt successfully
219+
// in case DSP does not want to enable domain name check
220+
[Fact]
221+
public void TokenIsCstgDerivedNoDomainNameTest()
222+
{
223+
_client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY }));
224+
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build();
225+
var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
226+
var res = _client.Decrypt(advertisingToken);
227+
Assert.True(res.IsClientSideGenerated);
228+
Assert.True(res.Success);
229+
Assert.Equal(DecryptionStatus.Success, res.Status);
230+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
231+
}
214232

215233
[Theory]
216234
// Any domain name is OK, because the token is not client-side generated.
@@ -224,7 +242,7 @@ public void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainName)
224242
var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(false).Build();
225243
string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build();
226244
ValidateAdvertisingToken(advertisingToken, IdentityScope.UID2, IdentityType.Email);
227-
var res = _client.Decrypt(advertisingToken, NOW, domainName);
245+
var res = _client.Decrypt(advertisingToken, domainName);
228246
Assert.False(res.IsClientSideGenerated);
229247
Assert.True(res.Success);
230248
Assert.Equal(DecryptionStatus.Success, res.Status);

0 commit comments

Comments
 (0)