Skip to content

Commit 3622ba2

Browse files
Added EUID and V4 tokens
1 parent 7c97dde commit 3622ba2

File tree

6 files changed

+54
-15
lines changed

6 files changed

+54
-15
lines changed

tests/test_encryption.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,23 @@ def test_encrypt_token_v3(self):
323323
identity_scope = IdentityScope.UID2
324324
now = dt.datetime.now(tz=timezone.utc)
325325

326+
keys = EncryptionKeysCollection([_master_key, _site_key, _keyset_key], default_keyset_id=20,
327+
master_keyset_id=9999, caller_site_id=20)
328+
329+
result = encrypt_key(uid2, identity_scope, keys, now=now, ad_token_version=AdvertisingTokenVersion.ADVERTISING_TOKEN_V3)
330+
final = decrypt_token(result, keys, now=now)
331+
332+
self.assertEqual(uid2, final.uid2)
333+
334+
def test_encrypt_token_v4(self):
335+
uid2 = "Y29keWlzZ3JlYXQ="
336+
identity_scope = IdentityScope.UID2
337+
now = dt.datetime.now(tz=timezone.utc)
338+
326339
keys = EncryptionKeysCollection([_master_key, _site_key, _keyset_key], default_keyset_id=20,
327340
master_keyset_id=9999, caller_site_id=20)
328341

329342
result = encrypt_key(uid2, identity_scope, keys, now=now)
330-
print(result)
331343
final = decrypt_token(result, keys, now=now)
332344

333345
self.assertEqual(uid2, final.uid2)

uid2_client/advertising_token_version.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from enum import Enum
1+
from enum import IntEnum
22

3-
class AdvertisingTokenVersion(Enum):
3+
class AdvertisingTokenVersion(IntEnum):
44
# showing as "AHA..." in the Base64 Encoding (Base64 'H' is 000111 and 112 is 01110000)
55
ADVERTISING_TOKEN_V3 = 112
66
# showing as "AIA..." in the Base64URL Encoding ('H' is followed by 'I' hence

uid2_client/client.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,21 @@ def refresh_keys(self):
6363
EncryptionKeysCollection containing the keys
6464
"""
6565
req, nonce = self._make_v2_request(dt.datetime.now(tz=timezone.utc))
66-
print(req)
6766
resp = self._post('/v2/key/sharing', headers=self._auth_headers(), data=req)
6867
resp_body = json.loads(self._parse_v2_response(resp.read(), nonce)).get('body')
69-
keys = [EncryptionKey(k['id'], k.get('site_id', -1), _make_dt(k['created']), _make_dt(k['activates']),
70-
_make_dt(k['expires']), base64.b64decode(k['secret']), k['keyset_id'])
71-
for k in resp_body["keys"]]
68+
keys = []
69+
for key in resp_body["keys"]:
70+
keyset_id = None
71+
if "keyset_id" in key:
72+
keyset_id = key["keyset_id"]
73+
key = EncryptionKey(key['id'],
74+
key.get('site_id', -1),
75+
_make_dt(key['created']),
76+
_make_dt(key['activates']),
77+
_make_dt(key['expires']),
78+
base64.b64decode(key['secret']),
79+
keyset_id)
80+
keys.append(key)
7281

7382
return EncryptionKeysCollection(keys, resp_body["caller_site_id"], resp_body["master_keyset_id"],
7483
resp_body["default_keyset_id"], resp_body["token_expiry_seconds"])

uid2_client/encryption.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
from uid2_client.advertising_token_version import AdvertisingTokenVersion
1616
from uid2_client.uid2_base64_url_coder import Uid2Base64UrlCoder
17+
from uid2_client.identity_type import IdentityType
18+
from uid2_client.identity_scope import IdentityScope
1719

1820
encryption_block_size = AES.block_size
1921
"""int: block size for encryption routines
@@ -146,7 +148,8 @@ def _decrypt_token_v3(token_bytes, keys, now):
146148

147149
return DecryptedToken(id_str, established, site_id, site_key.site_id)
148150

149-
def _encrypt_token_v3(uid2, identity_scope, master_key, site_key, site_id, now, token_expiry):
151+
152+
def _encrypt_token(uid2, identity_scope, master_key, site_key, site_id, now, token_expiry, ad_token_version):
150153
site_payload = bytearray(128)
151154
#Site id
152155
site_payload[0:4] = int.to_bytes(site_id, byteorder='big', length=4)
@@ -171,11 +174,16 @@ def _encrypt_token_v3(uid2, identity_scope, master_key, site_key, site_id, now,
171174
encrypted_master_payload = _encrypt_gcm(bytes(master_payload), None, master_key.secret)
172175

173176
root_writer = bytearray(len(encrypted_master_payload)+6)
174-
root_writer[0:1] = int.to_bytes(0, byteorder='big', length=1)
175-
root_writer[1:2] = int.to_bytes(112, byteorder='big', length=1)
177+
first_char = uid2[0]
178+
identity_type = IdentityType.Phone if first_char == 'F' or first_char == 'B' else IdentityType.Email
179+
root_writer[0:1] = int.to_bytes((int(identity_scope) << 4 | int(identity_type) << 2), byteorder='big', length=1)
180+
root_writer[1:2] = int.to_bytes(ad_token_version, byteorder='big', length=1)
176181
root_writer[2:6] = int.to_bytes(master_key.key_id, byteorder='big', length=4)
177182
root_writer[6:] = bytes(encrypted_master_payload)
178183

184+
if ad_token_version == AdvertisingTokenVersion.ADVERTISING_TOKEN_V4:
185+
return Uid2Base64UrlCoder.encode(root_writer)
186+
179187
return base64.b64encode(root_writer)
180188

181189

@@ -185,15 +193,25 @@ def encrypt_key(uid2, indentity_scope, keys, keyset_id=None, **kwargs):
185193
186194
Args:
187195
uid2: the uid2 to be encrypted
196+
indentity_scope (IdentityScope): If the key will be uid2 or euid2
188197
keys (EncryptionKeysCollection): collection of keys to choose from for encryption
189198
keyset_id (int) : An optional keyset id to use for the encryption. Will use default keyset if left blank
190199
200+
Keyword Args:
201+
now (Datetime): the datettime to use for now. Defaults to utc now
202+
ad_token_version (AdvertisingTokenVersion): Defaults to v4
203+
191204
Returns (str): Sharing Token
192205
193206
"""
194207
now = kwargs.get("now")
195208
if now is None:
196209
now = dt.datetime.now(tz=timezone.utc)
210+
211+
ad_token_version = kwargs.get("ad_token_version")
212+
if ad_token_version is None:
213+
ad_token_version = AdvertisingTokenVersion.ADVERTISING_TOKEN_V4
214+
197215
key = keys.get_default_keyset_key(now) if keyset_id is None else keys.get_by_keyset_key(keyset_id, now)
198216
master_key = keys.get_by_keyset_key(9999, now)
199217

@@ -209,7 +227,7 @@ def encrypt_key(uid2, indentity_scope, keys, keyset_id=None, **kwargs):
209227
print("No Keyset Key found")
210228
return
211229

212-
return _encrypt_token_v3(uid2, indentity_scope, master_key, key, site_id, now, token_expiry)
230+
return _encrypt_token(uid2, indentity_scope, master_key, key, site_id, now, token_expiry, ad_token_version)
213231

214232

215233
def encrypt_data(data, identity_scope, **kwargs):

uid2_client/identity_scope.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from enum import Enum
1+
from enum import IntEnum
22

33

4-
class IdentityScope(Enum):
4+
class IdentityScope(IntEnum):
55
"""Enum for types of unified ID"""
66
UID2 = 0
77
EUID = 1

uid2_client/identity_type.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from enum import Enum
1+
from enum import IntEnum
22

3-
class IdentityType(Enum):
3+
class IdentityType(IntEnum):
44
"""Enum for types of ID source"""
55
Email = 0
66
Phone = 1

0 commit comments

Comments
 (0)