Skip to content

Commit 1acf9c3

Browse files
authored
jon-UID2-1207-consistent-token-prefixes (#10)
1 parent eeb44fe commit 1acf9c3

File tree

2 files changed

+34
-10
lines changed

2 files changed

+34
-10
lines changed

tests/test_encryption.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,28 @@ def cross_platform_consistency_check_base64_url_test(self, raw_input, expected_b
4848
decoded = Uid2Base64UrlCoder.decode(base64_url_encoded_str)
4949
self.assertEqual(decoded, raw_input)
5050

51+
def validate_advertising_token(self, advertising_token_string, identity_scope, identity_type):
52+
first_char = advertising_token_string[0]
53+
if identity_scope == IdentityScope.UID2:
54+
self.assertEqual("A" if identity_type == IdentityType.Email.value else "B", first_char)
55+
else:
56+
self.assertEqual("E" if identity_type == IdentityType.Email.value else "F", first_char)
57+
58+
second_char = advertising_token_string[1]
59+
self.assertEqual("4", second_char)
60+
61+
# No URL-unfriendly characters allowed
62+
self.assertEqual(-1, advertising_token_string.find("="))
63+
self.assertEqual(-1, advertising_token_string.find("+"))
64+
self.assertEqual(-1, advertising_token_string.find("/"))
65+
66+
67+
def generate_uid2_token_v4(self, uid, master_key, site_id, site_key, params = Params(), identity_type = IdentityType.Email, identity_scope = IdentityScope.UID2):
68+
advertising_token = UID2TokenGenerator.generate_uid2_token_v4(uid, master_key, site_id, site_key, params)
69+
self.validate_advertising_token(advertising_token, identity_scope, identity_type)
70+
return advertising_token
71+
72+
5173
def test_cross_platform_consistency_decrypt(self):
5274
crossPlatformAdvertisingToken = "AIAAAACkOqJj9VoxXJNnuX3v-ymceRf8_Av0vA5asOj9YBZJc1kV1vHdmb0AIjlzWnFF-gxIlgXqhRFhPo3iXpugPBl3gv4GKnGkw-Zgm2QqMsDPPLpMCYiWrIUqHPm8hQiq9PuTU-Ba9xecRsSIAN0WCwKLwA_EDVdzmnLJu64dQoeYmuu3u1G2EuTkuMrevmP98tJqSUePKwnfK73-0Zdshw";
5375
# Sunday, 1 January 2023 1:01:01 AM UTC
@@ -78,7 +100,7 @@ def test_cross_platform_consistency_decrypt(self):
78100
params = Params(dt.timedelta(days=1 * 365 * 20))
79101

80102
# verify that the dynamically created ad token can be decrypted
81-
runtime_advertising_token = UID2TokenGenerator.generate_uid2_token_v4(_example_id, master_key, _site_id, site_key, params)
103+
runtime_advertising_token = self.generate_uid2_token_v4(_example_id, master_key, _site_id, site_key, params)
82104
# best effort check as the token might simply just not require padding
83105
self.assertEqual(-1, runtime_advertising_token.find('='))
84106
self.assertEqual(-1, runtime_advertising_token.find('+'))
@@ -91,8 +113,9 @@ def test_cross_platform_consistency_decrypt(self):
91113
result = decrypt(crossPlatformAdvertisingToken, EncryptionKeysCollection([_master_key, _site_key]))
92114
self.assertEqual(_example_id, result.uid2)
93115

116+
94117
def test_decrypt_token_v4(self):
95-
token = UID2TokenGenerator.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key)
118+
token = self.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key)
96119

97120
keys = EncryptionKeysCollection([_master_key, _site_key])
98121
result = decrypt(token, keys)
@@ -101,7 +124,7 @@ def test_decrypt_token_v4(self):
101124

102125

103126
def test_decrypt_token_v4_empty_keys(self):
104-
token = UID2TokenGenerator.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key)
127+
token = self.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key)
105128

106129
keys = EncryptionKeysCollection([])
107130

@@ -110,7 +133,7 @@ def test_decrypt_token_v4_empty_keys(self):
110133

111134

112135
def test_decrypt_token_v4_no_master_key(self):
113-
token = UID2TokenGenerator.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key)
136+
token = self.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key)
114137

115138
keys = EncryptionKeysCollection([_site_key])
116139

@@ -119,7 +142,7 @@ def test_decrypt_token_v4_no_master_key(self):
119142

120143

121144
def test_decrypt_token_v4_no_site_key(self):
122-
token = UID2TokenGenerator.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key)
145+
token = self.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key)
123146

124147
keys = EncryptionKeysCollection([_master_key])
125148

@@ -138,7 +161,7 @@ def test_decrypt_token_v4_invalid_version(self):
138161

139162
def test_decrypt_token_v4_expired(self):
140163
params = Params(dt.timedelta(seconds=-1))
141-
token = UID2TokenGenerator.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key, params)
164+
token = self.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key, params)
142165

143166
keys = EncryptionKeysCollection([_master_key, _site_key])
144167

@@ -149,7 +172,7 @@ def test_decrypt_token_v4_expired(self):
149172
def test_decrypt_token_v4_custom_now(self):
150173
expiry = dt.datetime(2021, 3, 22, 9, 1, 2, tzinfo=timezone.utc)
151174
params = Params(expiry)
152-
token = UID2TokenGenerator.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key, params)
175+
token = self.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key, params)
153176

154177
keys = EncryptionKeysCollection([_master_key, _site_key])
155178

@@ -162,7 +185,7 @@ def test_decrypt_token_v4_custom_now(self):
162185

163186
def test_decrypt_token_v4_invalid_payload(self):
164187
params = Params(dt.timedelta(seconds=-1))
165-
token = UID2TokenGenerator.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key, params)
188+
token = self.generate_uid2_token_v4(_example_id, _master_key, _site_id, _site_key, params)
166189

167190
keys = EncryptionKeysCollection([_master_key, _site_key])
168191

@@ -596,6 +619,7 @@ def test_decrypt_data_v2(self):
596619
self.assertEqual(format_time(now), format_time(decrypted.encrypted_at))
597620

598621

622+
# TODO - deduplicate the logic in sharing_test.py that has been copied from this file
599623
def test_raw_uid_produces_correct_identity_type_in_token(self):
600624
#v2 +12345678901. Although this was generated from a phone number, it's a v2 raw UID which doesn't encode this
601625
# information, so token assumes email by default.
@@ -611,7 +635,7 @@ def test_raw_uid_produces_correct_identity_type_in_token(self):
611635
IdentityType.Email.value) #v3 EUID [email protected]
612636

613637
def verify_identity_type(self, raw_uid, expected_identity_type):
614-
token = UID2TokenGenerator.generate_uid2_token_v4(raw_uid, _master_key, _site_id, _site_key)
638+
token = self.generate_uid2_token_v4(raw_uid, _master_key, _site_id, _site_key, Params(), expected_identity_type)
615639
keys = EncryptionKeysCollection([_master_key, _site_key])
616640
result = decrypt(token, keys)
617641
self.assertEqual(raw_uid, result.uid2)

tests/uid2_token_generator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def generate_uid2_token_with_debug_info(id_str, master_key, site_id, site_key, p
8989
if (first_char == 'F') or (first_char == 'B'):
9090
identity_type = IdentityType.Phone.value
9191

92-
token = int.to_bytes((params.identity_scope << 4 | identity_type << 2), 1, 'big')
92+
token = int.to_bytes((params.identity_scope << 4 | identity_type << 2) | 3, 1, 'big')
9393
token += int.to_bytes(version, 1, 'big')
9494
token += int.to_bytes(master_key.key_id, 4, 'big')
9595
token += _encrypt_gcm(master_payload, None, master_key.secret)

0 commit comments

Comments
 (0)