11import base64
2+ import os
3+ from enum import Enum
24
5+ from Crypto .Cipher import AES
36from datetime import timezone
4- import os
5- from uid2_client import encryption_block_size
6- from uid2_client .advertising_token_version import AdvertisingTokenVersion
7- from uid2_client .encryption import _encrypt_data_v1 , _encrypt_gcm , _PayloadType
7+ import datetime as dt
88from uid2_client .identity_scope import IdentityScope
9+
10+
11+ from uid2_client .advertising_token_version import AdvertisingTokenVersion
912from uid2_client .identity_type import IdentityType
10- from uid2_client .keys import *
1113from uid2_client .uid2_base64_url_coder import Uid2Base64UrlCoder
1214
15+ encryption_block_size = AES .block_size
16+ """int: block size for encryption routines
17+
18+ This determines the size of initialization vectors (IV), required data padding, etc.
19+ """
20+
21+
22+ class _PayloadType (Enum ):
23+ """Enum for types of payload that can be encoded in opaque strings"""
24+ ENCRYPTED_DATA = 128
25+ ENCRYPTED_DATA_V3 = 96
26+
27+
28+ def _add_pkcs7_padding (data , block_size ):
29+ pad_len = block_size - (len (data ) % block_size )
30+ return data + bytes ([pad_len ]) * pad_len
31+
32+
33+ def _encrypt_gcm (data , iv , secret ):
34+ if iv is None :
35+ iv = os .urandom (12 )
36+ elif len (iv ) != 12 :
37+ raise ValueError ("iv must be 12 bytes" )
38+ cipher = AES .new (secret , AES .MODE_GCM , nonce = iv )
39+ ciphertext , tag = cipher .encrypt_and_digest (data )
40+ return cipher .nonce + ciphertext + tag
41+
42+
43+ def _encrypt (data , iv , key ):
44+ cipher = AES .new (key .secret , AES .MODE_CBC , IV = iv )
45+ return cipher .encrypt (_add_pkcs7_padding (data , AES .block_size ))
46+
47+
48+ def _encrypt_data_v1 (data , key , iv ):
49+ return int .to_bytes (key .key_id , 4 , 'big' ) + iv + _encrypt (data , iv , key )
50+
1351
1452class Params :
1553 def __init__ (self , expiry = dt .datetime .now (tz = timezone .utc ) + dt .timedelta (hours = 1 ),
16- identity_scope = IdentityScope .UID2 .value , token_created_at = dt .datetime .now (tz = timezone .utc )):
54+ identity_scope = IdentityScope .UID2 .value , token_generated_at = dt .datetime .now (tz = timezone .utc )):
1755 self .identity_scope = identity_scope
1856 self .token_expiry = expiry
19- self .token_created_at = token_created_at
57+ self .token_generated_at = token_generated_at
2058 if not isinstance (expiry , dt .datetime ):
2159 self .token_expiry = dt .datetime .now (tz = timezone .utc ) + expiry
2260
@@ -26,6 +64,7 @@ def default_params():
2664
2765
2866class UID2TokenGenerator :
67+
2968 @staticmethod
3069 def generate_uid2_token_v2 (id_str , master_key , site_id , site_key , params = default_params (), version = 2 ):
3170 id = bytes (id_str , 'utf-8' )
@@ -34,7 +73,7 @@ def generate_uid2_token_v2(id_str, master_key, site_id, site_key, params = defau
3473 identity += id
3574 # old privacy_bits
3675 identity += int .to_bytes (0 , 4 , 'big' )
37- created = params .token_created_at
76+ created = params .token_generated_at
3877 identity += int .to_bytes (int (created .timestamp ()) * 1000 , 8 , 'big' )
3978 identity_iv = bytes ([10 , 11 , 12 , 13 , 14 , 15 , 16 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ])
4079 expiry = params .token_expiry
@@ -48,12 +87,12 @@ def generate_uid2_token_v2(id_str, master_key, site_id, site_key, params = defau
4887 return base64 .b64encode (token ).decode ('ascii' )
4988
5089 @staticmethod
51- def generate_uid2_token_v3 (id_str , master_key , site_id , site_key , params = default_params ()):
90+ def generate_uid2_token_v3 (id_str , master_key , site_id , site_key , params = default_params ()):
5291 return UID2TokenGenerator .generate_uid2_token_with_debug_info (id_str , master_key , site_id , site_key , params ,
5392 AdvertisingTokenVersion .ADVERTISING_TOKEN_V3 .value )
5493
5594 @staticmethod
56- def generate_uid2_token_v4 (id_str , master_key , site_id , site_key , params = default_params ()):
95+ def generate_uid2_token_v4 (id_str , master_key , site_id , site_key , params = default_params ()):
5796 return UID2TokenGenerator .generate_uid2_token_with_debug_info (id_str , master_key , site_id , site_key , params ,
5897 AdvertisingTokenVersion .ADVERTISING_TOKEN_V4 .value )
5998
@@ -63,7 +102,7 @@ def generate_uid_token(id_str, master_key, site_id, site_key, identity_scope, to
63102 params = default_params ()
64103 params .identity_scope = identity_scope
65104 if created_at is not None :
66- params .token_created_at = created_at
105+ params .token_generated_at = created_at
67106 if expires_at is not None :
68107 params .token_expiry = expires_at
69108 if token_version == AdvertisingTokenVersion .ADVERTISING_TOKEN_V2 :
@@ -77,29 +116,29 @@ def generate_uid_token(id_str, master_key, site_id, site_key, identity_scope, to
77116
78117 @staticmethod
79118 def generate_uid2_token_with_debug_info (id_str , master_key , site_id , site_key , params , version ):
80- id = base64 .b64decode (id_str )
81119
82- site_payload = int .to_bytes (site_id , 4 , 'big' )
83- site_payload += int .to_bytes (0 , 8 , 'big' ) # publisher id
84- site_payload += int .to_bytes (0 , 4 , 'big' ) # client key id
120+ # Publisher Data
121+ site_payload = int .to_bytes (site_id , length = 4 , byteorder = 'big' )
122+ site_payload += int .to_bytes (0 , length = 8 , byteorder = 'big' ) # publisher id
123+ site_payload += int .to_bytes (0 , length = 4 , byteorder = 'big' ) # client key id
85124
86- site_payload += int .to_bytes (0 , 4 , 'big' ) # privacy bits
87- created = params .token_created_at
88- site_payload += int .to_bytes (int (created .timestamp ()) * 1000 , 8 , 'big' ) # established
89- site_payload += int .to_bytes (int (created .timestamp ()) * 1000 , 8 , 'big' ) # refreshed
90- site_payload += id
125+ # User Identity Data
126+ site_payload += int .to_bytes (0 , length = 4 , byteorder = 'big' ) # privacy bits
127+ generated_at_timestamp = int (params .token_generated_at .timestamp ()) * 1000
128+ site_payload += int .to_bytes (generated_at_timestamp , length = 8 , byteorder = 'big' ) # established
129+ site_payload += int .to_bytes (generated_at_timestamp , length = 8 , byteorder = 'big' ) # last refreshed/generated
130+ site_payload += base64 .b64decode (id_str )
91131
92- expiry = params .token_expiry
93-
94- master_payload = int .to_bytes (int (expiry .timestamp ()) * 1000 , 8 , 'big' )
95- master_payload += int .to_bytes (int (created .timestamp ()) * 1000 , 8 , 'big' ) # created
132+ master_payload = int .to_bytes (int (params .token_expiry .timestamp ()) * 1000 , length = 8 , byteorder = 'big' ) # expiry
133+ master_payload += int .to_bytes (generated_at_timestamp , length = 8 , byteorder = 'big' ) # created
96134
97- master_payload += int .to_bytes (0 , 4 , 'big' ) # operator site id
98- master_payload += int .to_bytes (0 , 1 , 'big' ) # operator type
99- master_payload += int .to_bytes (0 , 4 , 'big' ) # operator version
100- master_payload += int .to_bytes (0 , 4 , 'big' ) # operator key id
135+ # Operator Identity Data
136+ master_payload += int .to_bytes (0 , length = 4 , byteorder = 'big' ) # site id
137+ master_payload += int .to_bytes (1 , length = 1 , byteorder = 'big' ) # operator type
138+ master_payload += int .to_bytes (0 , length = 4 , byteorder = 'big' ) # operator version
139+ master_payload += int .to_bytes (0 , length = 4 , byteorder = 'big' ) # operator key id
101140
102- master_payload += int .to_bytes (site_key .key_id , 4 , 'big' )
141+ master_payload += int .to_bytes (site_key .key_id , length = 4 , byteorder = 'big' ) # Site Key ID
103142 master_payload += _encrypt_gcm (site_payload , None , site_key .secret )
104143
105144 first_char = id_str [0 ]
@@ -108,9 +147,9 @@ def generate_uid2_token_with_debug_info(id_str, master_key, site_id, site_key, p
108147 if (first_char == 'F' ) or (first_char == 'B' ):
109148 identity_type = IdentityType .Phone .value
110149
111- token = int .to_bytes ((params .identity_scope << 4 | identity_type << 2 ) | 3 , 1 , 'big' )
112- token += int .to_bytes (version , 1 , 'big' )
113- token += int .to_bytes (master_key .key_id , 4 , 'big' )
150+ token = int .to_bytes ((params .identity_scope << 4 | identity_type << 2 ) | 3 , length = 1 , byteorder = 'big' )
151+ token += int .to_bytes (version , length = 1 , byteorder = 'big' )
152+ token += int .to_bytes (master_key .key_id , length = 4 , byteorder = 'big' )
114153 token += _encrypt_gcm (master_payload , None , master_key .secret )
115154
116155 if version == AdvertisingTokenVersion .ADVERTISING_TOKEN_V4 .value :
0 commit comments