spotifywebapiPython 1.0.71__py3-none-any.whl → 1.0.73__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of spotifywebapiPython might be problematic. Click here for more details.

Files changed (25) hide show
  1. {spotifywebapiPython-1.0.71.dist-info → spotifywebapiPython-1.0.73.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.71.dist-info → spotifywebapiPython-1.0.73.dist-info}/RECORD +25 -25
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +16 -2
  5. spotifywebapipython/docs/index.html +2 -2
  6. spotifywebapipython/docs/search.js +1 -1
  7. spotifywebapipython/docs/spotifywebapiPython/const.html +3 -3
  8. spotifywebapipython/docs/spotifywebapiPython/saappmessages.html +2 -2
  9. spotifywebapipython/docs/spotifywebapiPython/sautils.html +2 -2
  10. spotifywebapipython/docs/spotifywebapiPython/spotifyapierror.html +2 -2
  11. spotifywebapipython/docs/spotifywebapiPython/spotifyapimessage.html +2 -2
  12. spotifywebapipython/docs/spotifywebapiPython/spotifyauthtoken.html +2 -2
  13. spotifywebapipython/docs/spotifywebapiPython/spotifyclient.html +8 -2
  14. spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +2 -2
  15. spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
  16. spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
  17. spotifywebapipython/models/zeroconfdiscoveryresult.py +13 -0
  18. spotifywebapipython/spotifyclient.py +64 -19
  19. spotifywebapipython/zeroconfapi/blobbuilder.py +327 -66
  20. spotifywebapipython/zeroconfapi/cryptodiffiehellman.py +71 -6
  21. spotifywebapipython/zeroconfapi/helpers.py +34 -7
  22. spotifywebapipython/zeroconfapi/zeroconfconnect.py +50 -6
  23. {spotifywebapiPython-1.0.71.dist-info → spotifywebapiPython-1.0.73.dist-info}/LICENSE +0 -0
  24. {spotifywebapiPython-1.0.71.dist-info → spotifywebapiPython-1.0.73.dist-info}/WHEEL +0 -0
  25. {spotifywebapiPython-1.0.71.dist-info → spotifywebapiPython-1.0.73.dist-info}/top_level.txt +0 -0
@@ -8,92 +8,353 @@ from base64 import b64decode, b64encode
8
8
 
9
9
  from .credentials import Credentials
10
10
  from .cryptodiffiehellman import CryptoDiffieHellman
11
- from .helpers import write_bytes, write_int, byte_list_to_int, b64_to_int, int_to_bytes
11
+ from .helpers import write_bytes, write_int, byte_list_to_int, b64_to_int, int_to_bytes, string_to_int
12
+
13
+ # get smartinspect logger reference; create a new session for this module name.
14
+ from smartinspectpython.siauto import SIAuto, SILevel, SISession, SIMethodParmListContext, SISourceId
15
+ import logging
16
+ _logsi:SISession = SIAuto.Si.GetSession(__name__)
17
+ if (_logsi == None):
18
+ _logsi = SIAuto.Si.AddSession(__name__, True)
19
+ #_logsi.SystemLogger = logging.getLogger(__name__)
12
20
 
13
21
 
14
22
  class BlobBuilder:
15
23
 
16
- def __init__(self, credentials: Credentials, device_id: str, remote_pubkey: str):
17
- self.credentials: Credentials = credentials
18
- self.device_id: bytes = bytes(device_id, 'ascii')
19
- self.remote_pubkey: str = remote_pubkey
20
- self.dh_keys = CryptoDiffieHellman()
21
- self._blob = b''
22
- self._encrypted_blob = b''
24
+ AES_KEY_SIZE:int = 16 # 16 byte / 128 bit key size for AES encryption
25
+ AES_BLOCK_SIZE:int = 16 # 16 byte / 128 bit block size for AES encryption
26
+
27
+ def __init__(self, credentials:Credentials, device_id:str, remotePublicKeyBase64String:str):
28
+ """
29
+ Initializes a new instance of the class.
30
+
31
+ Args:
32
+ credentials (Credentials):
33
+ deviceId (str):
34
+ remotePublicKeyBase64String (str):
35
+ Remote public key value in a base64-encoded string format.
36
+ """
37
+
38
+ # initialize storage.
39
+ self._EncryptedBlob:bytes = b''
40
+ self._EncryptedBlobSigned:bytes = b''
41
+ self._EncryptedBlobSignedBase64String:str = ''
42
+ self._RemotePublicKeyBase64String:str = remotePublicKeyBase64String
43
+ self._RemotePublicKey:int = b64_to_int(remotePublicKeyBase64String)
44
+
23
45
  # formulate origin device information.
24
- self._OriginDeviceName = 'ha-spotifyplus'
25
- self._OriginDeviceId = hashlib.sha1(bytes(self._OriginDeviceName, 'ascii')).digest().hex()
46
+ self._OriginDeviceName:str = 'ha-spotifyplus'
47
+ self._OriginDeviceId:str = hashlib.sha1(bytes(self._OriginDeviceName, 'ascii')).digest().hex()
48
+
49
+ self.credentials:Credentials = credentials
50
+ self.device_id:bytes = bytes(device_id, 'ascii')
51
+ self.dh_keys = CryptoDiffieHellman()
52
+ self._blob:bytes = b''
53
+ self._decrypted_blob:bytes = b''
54
+
55
+
56
+ @property
57
+ def EncryptedBlob(self) -> bytes:
58
+ """
59
+ Returns the unsigned encrypted blob as a bytes object.
60
+ """
61
+ return self._EncryptedBlob
62
+
63
+
64
+ @property
65
+ def EncryptedBlobSigned(self) -> bytes:
66
+ """
67
+ Returns the signed encrypted blob as a bytes object.
68
+ """
69
+ return self._EncryptedBlobSigned
70
+
71
+
72
+ @property
73
+ def EncryptedBlobSignedBase64String(self) -> str:
74
+ """
75
+ Returns the signed encrypted blob as a base64 encoded string.
76
+ """
77
+ return self._EncryptedBlobSignedBase64String
78
+
79
+
80
+ @property
81
+ def RemotePublicKeyBase64String(self) -> str:
82
+ """
83
+ Returns the remote public key value in a base64 encoded string format.
84
+ """
85
+ return self._RemotePublicKeyBase64String
86
+
87
+
88
+ @property
89
+ def RemotePublicKey(self) -> int:
90
+ """
91
+ Returns the remote public key key value in an integer format.
92
+ """
93
+ return self._RemotePublicKey
26
94
 
27
95
 
28
96
  def build(self):
29
- self._build()
30
- self._encrypt()
31
- return self._encrypted_blob.decode("ascii")
32
-
33
-
34
- def _build(self): # -> bytes
35
- blob = bytearray()
36
- # 'I'
37
- write_int(0x49, blob)
38
- # username
39
- write_bytes(self.credentials.username, blob)
40
- # 'P'
41
- write_int(0x50, blob)
42
- # auth_type
43
- write_int(self.credentials.auth_type, blob)
44
- # 'Q'
45
- write_int(0x51, blob)
46
- # password
47
- write_bytes(self.credentials.password, blob)
48
- # Padding
49
- n_zeros = 16 - (len(blob) % 16) - 1
50
- blob.extend([0] * n_zeros)
51
- blob.append(n_zeros + 1)
52
-
53
- blen = len(blob)
54
- for i in range(blen - 0x11, -1, -1):
55
- blob[blen - i - 1] ^= blob[blen - i - 0x11]
97
+ """
98
+ Builds the blob, encrypts it, and returns the signed encrypted blob as a base64 encoded string.
99
+ """
100
+ self._BuildBlob()
101
+ self._Encrypt()
102
+ return self._EncryptedBlobSignedBase64String
103
+
104
+
105
+ def _BuildBlob(self): # -> bytes
106
+
107
+ apiMethodName:str = 'BlobBuilder._BuildBlob'
108
+ apiMethodParms:SIMethodParmListContext = None
109
+
110
+ try:
111
+
112
+ # trace.
113
+ apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
114
+ apiMethodParms.AppendKeyValue("device_id", self.device_id)
115
+ _logsi.LogMethodParmList(SILevel.Verbose, "Building Spotify Connect ZeroConf addUser Blob", apiMethodParms)
116
+
117
+ # create the blob data that will be sent to the device.
118
+ blob = bytearray()
119
+ # 'I'
120
+ write_int(0x49, blob)
121
+ # username
122
+ write_bytes(self.credentials.username, blob)
123
+ # 'P'
124
+ write_int(0x50, blob)
125
+ # auth_type
126
+ write_int(self.credentials.auth_type, blob)
127
+ # 'Q'
128
+ write_int(0x51, blob)
129
+ # password
130
+ write_bytes(self.credentials.password, blob)
131
+ _logsi.LogBinary(SILevel.Verbose, "Blob Data, Unencrypted Bytes", blob)
132
+ # padding
133
+ n_zeros = BlobBuilder.AES_BLOCK_SIZE - (len(blob) % BlobBuilder.AES_BLOCK_SIZE) - 1
134
+ blob.extend([0] * n_zeros)
135
+ blob.append(n_zeros + 1)
136
+ _logsi.LogBinary(SILevel.Verbose, "Blob Data, Unencrypted Bytes (with padding)", blob)
137
+
138
+ # not sure what the following is really trying to accomplish.
139
+ # it leaves the first 16 bytes as-is, then starts performing a Bitwise XOR on
140
+ # on each byte remaining in the buffer (starting at offset 0x11 / byte 17).
141
+ blen = len(blob)
142
+ for i in range(blen - 0x11, -1, -1):
143
+ blob[blen - i - 1] ^= blob[blen - i - 0x11]
144
+ _logsi.LogBinary(SILevel.Verbose, "Blob Data, Unencrypted Bytes after XOR of bytes 16 thru %d" % (blen), blob)
56
145
 
57
- secret = hashlib.sha1(self.device_id).digest()
146
+ # secret is the deviceId value.
147
+ secret = hashlib.sha1(self.device_id).digest()
148
+ _logsi.LogBinary(SILevel.Verbose, "Blob Data Secret, SHA1 Digest", secret)
58
149
 
59
- keys = PBKDF2(secret, self.credentials.username, 20, count=0x100, hmac_hash_module=SHA1)
60
- key = bytearray(hashlib.sha1(keys).digest()[:20])
61
- key.extend(bytearray([0x00, 0x00, 0x00, 0x14]))
150
+ # create a unique, strong hash for the given secret.
151
+ keys = PBKDF2(secret, self.credentials.username, 20, count=0x100, hmac_hash_module=SHA1)
152
+ key = bytearray(hashlib.sha1(keys).digest()[:20])
153
+ key.extend(bytearray([0x00, 0x00, 0x00, 0x14]))
154
+ _logsi.LogBinary(SILevel.Verbose, "Blob Data Encryption Key", key)
62
155
 
63
- encrypted_blob = bytearray()
64
- cipher = AES.new(key, mode=AES.MODE_ECB)
65
- block_size = 16
156
+ # create a new AES cipher that will be used to encrypt the data.
157
+ # use the hashed secret as the encryption key.
158
+ # data will be encrypted in 16-byte blocks (or chunks).
159
+ encrypted_blob = bytearray()
160
+ cipher = AES.new(key, mode=AES.MODE_ECB)
66
161
 
67
- def chunker(seq, size):
68
- return (seq[pos:pos + size] for pos in range(0, len(seq), size))
162
+ def chunker(seq, size):
163
+ return (seq[pos:pos + size] for pos in range(0, len(seq), size))
69
164
 
70
- for chunk in chunker(blob, block_size):
71
- encrypted_blob.extend(cipher.encrypt(chunk))
165
+ # encrypt the data.
166
+ for chunk in chunker(blob, BlobBuilder.AES_BLOCK_SIZE):
167
+ encrypted_blob.extend(cipher.encrypt(chunk))
168
+
169
+ # trace.
170
+ _logsi.LogBinary(SILevel.Verbose, "Blob Data, Encrypted Bytes", encrypted_blob)
72
171
 
73
- self._blob = b64encode(encrypted_blob)
172
+ # convert encrypted bytes to a base64-encoded string so we can
173
+ # send it to the device via an http request.
174
+ self._blob = b64encode(encrypted_blob)
175
+ _logsi.LogBinary(SILevel.Verbose, "Blob Data, Encrypted base64-encoded bytes", self._blob)
74
176
 
177
+ finally:
178
+
179
+ # trace.
180
+ _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
75
181
 
76
- def _encrypt(self):
77
- remote_device_key = b64_to_int(self.remote_pubkey)
78
- shared_key = self.dh_keys.shared_secret(remote_device_key)
79
182
 
80
- b_shared_key = int_to_bytes(shared_key)
81
- base_key = hashlib.sha1(b_shared_key).digest()[:16]
183
+ def _GetIVectorAES128(self) -> bytes:
184
+ """
185
+ Returns a new initialization vector for AES (128-bit) cryptographic functions.
186
+
187
+ Returns:
188
+ A new initialization vector of 16 bytes (128-bits).</returns>
189
+ """
190
+ iv_data:list = [253, 81, 222, 19, 70, 203, 45, 89, 141, 68, 210, 240, 93, 20, 76, 30]
191
+ return int.from_bytes(bytes(iv_data), 'big')
192
+
193
+
194
+ def _Encrypt(self):
195
+ """
196
+ Encrypts the `_blob` argument and
197
+ """
198
+
199
+ apiMethodName:str = 'BlobBuilder.Encrypt'
200
+ apiMethodParms:SIMethodParmListContext = None
201
+
202
+ try:
203
+
204
+ # trace.
205
+ apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
206
+ apiMethodParms.AppendKeyValue("RemotePublicKeyBase64String", self.RemotePublicKeyBase64String)
207
+ apiMethodParms.AppendKeyValue("RemotePublicKey", self.RemotePublicKey)
208
+ _logsi.LogMethodParmList(SILevel.Verbose, "Encrypt Spotify Connect ZeroConf addUser Blob", apiMethodParms)
209
+
210
+ # trace.
211
+ _logsi.LogInt(SILevel.Verbose, "Remote Public Key: %s" % self.RemotePublicKey, self.RemotePublicKey)
212
+
213
+ # create shared key.
214
+ shared_key:int = self.dh_keys.SharedSecret(self.RemotePublicKey)
215
+ _logsi.LogInt(SILevel.Verbose, "Shared Key: %s" % shared_key, shared_key)
216
+
217
+ b_shared_key:bytes = int_to_bytes(shared_key)
218
+ _logsi.LogBinary(SILevel.Verbose, "Shared Key bytes", b_shared_key)
219
+ base_key:bytes = hashlib.sha1(b_shared_key).digest()[:16]
220
+ _logsi.LogBinary(SILevel.Verbose, "Base Key bytes", base_key)
82
221
 
83
- checksum_key = hmac.new(base_key, b'checksum', 'sha1').digest()
84
- encryption_key = hmac.new(base_key, b'encryption', 'sha1').digest()
222
+ # create encryption key (16 byte / 128 bit key size for AES encryption).
223
+ encryption_key:bytes = hmac.new(base_key, b'encryption', 'sha1').digest()
224
+ encryption_key = encryption_key[:BlobBuilder.AES_KEY_SIZE]
225
+ _logsi.LogBinary(SILevel.Verbose, "Encryption Key (AES128-bit) bytes", encryption_key)
85
226
 
86
- iv = [253, 81, 222, 19, 70, 203, 45, 89, 141, 68, 210, 240, 93, 20, 76, 30]
87
- ctr_e = Counter.new(128, initial_value=byte_list_to_int(iv))
88
- cipher = AES.new(encryption_key[:16], AES.MODE_CTR, counter=ctr_e)
89
- encrypted_blob = cipher.encrypt(self._blob)
227
+ # create new AES encryption cipher, and encrypt the data.
228
+ iv:list[int] = [253, 81, 222, 19, 70, 203, 45, 89, 141, 68, 210, 240, 93, 20, 76, 30]
229
+ ctr_e:dict = Counter.new(128, initial_value=self._GetIVectorAES128())
230
+ cipher = AES.new(encryption_key, AES.MODE_CTR, counter=ctr_e)
231
+ encrypted_blob:bytes = cipher.encrypt(self._blob)
232
+ _logsi.LogBinary(SILevel.Verbose, "Encrypted Blob (unsigned, bytes)", encrypted_blob)
90
233
 
91
- checksum = hmac.new(checksum_key, encrypted_blob, 'sha1').digest()
234
+ # create checksum key and checksum value of encrypted data.
235
+ checksum_key:bytes = hmac.new(base_key, b'checksum', 'sha1').digest()
236
+ _logsi.LogBinary(SILevel.Verbose, "Checksum Key: %s" % checksum_key.hex(), checksum_key)
237
+ checksum:bytes = hmac.new(checksum_key, encrypted_blob, 'sha1').digest()
238
+ _logsi.LogBinary(SILevel.Verbose, "Checksum Value (bytes)", checksum)
92
239
 
93
- encrypted_signed_blob = bytearray(iv)
94
- encrypted_signed_blob.extend(encrypted_blob)
95
- encrypted_signed_blob.extend(checksum)
240
+ # create signed encrypted data buffer that contains the encrypted data and checksum.
241
+ self._EncryptedBlobSigned:bytearray = bytearray(iv)
242
+ self._EncryptedBlobSigned.extend(encrypted_blob)
243
+ self._EncryptedBlobSigned.extend(checksum)
244
+ _logsi.LogBinary(SILevel.Verbose, "Encrypted Blob (signed with checksum, bytes)", self._EncryptedBlobSigned)
96
245
 
97
- self._encrypted_blob = b64encode(encrypted_signed_blob)
246
+ # convert encrypted bytes to a base64-encoded string.
247
+ b64result_bytes:bytes = b64encode(self._EncryptedBlobSigned)
248
+ self._EncryptedBlobSignedBase64String:str = b64result_bytes.decode('ascii')
249
+ _logsi.LogText(SILevel.Verbose, "Encrypted Blob (signed with checksum, base64-encoded string)", self._EncryptedBlobSignedBase64String)
98
250
 
99
-
251
+ finally:
252
+
253
+ # trace.
254
+ _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
255
+
256
+
257
+ # def _Decrypt(
258
+ # self,
259
+ # encrypted_blob_signed:str,
260
+ # ) -> None:
261
+ # """
262
+ # Decreypts the specified blob that was previously encrypted and signed by the
263
+ # `Encrypt` method.
264
+
265
+ # Args:
266
+ # encrypted_blob_signed (str):
267
+ # A base64 encoded string that represents a signed and encrypted data blob; the
268
+ # packet consists of the IV, data, and checksum blocks.
269
+ # """
270
+
271
+ # apiMethodName:str = 'BlobBuilder.Decrypt'
272
+ # apiMethodParms:SIMethodParmListContext = None
273
+
274
+ # try:
275
+
276
+ # # trace.
277
+ # apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
278
+ # apiMethodParms.AppendKeyValue("encrypted_blob_signed", encrypted_blob_signed)
279
+ # apiMethodParms.AppendKeyValue("RemotePublicKeyBase64String", self.RemotePublicKeyBase64String)
280
+ # _logsi.LogMethodParmList(SILevel.Verbose, "Decrypt Spotify Connect ZeroConf addUser Blob", apiMethodParms)
281
+
282
+ # if encrypted_blob_signed is not None:
283
+ # self._EncryptedBlobSignedBase64String = encrypted_blob_signed
284
+
285
+ # # convert base64-encoded string to bytes.
286
+ # _logsi.LogText(SILevel.Verbose, "Encrypted Blob (signed with checksum, base64-encoded string)", self._EncryptedBlobSignedBase64String)
287
+ # self._EncryptedBlobSigned:bytes = b64decode(self._EncryptedBlobSignedBase64String)
288
+ # _logsi.LogBinary(SILevel.Verbose, "Encrypted Blob (signed with checksum, bytes)", self._EncryptedBlobSigned)
289
+
290
+ # # get the individual components of the signed blob packet.
291
+ # iv = self._EncryptedBlobSigned[:16]
292
+ # self._EncryptedBlob = self._EncryptedBlobSigned[16:len(self._EncryptedBlobSigned)-20]
293
+ # checksum = self._EncryptedBlobSigned[len(self._EncryptedBlobSigned)-20:]
294
+ # _logsi.LogBinary(SILevel.Verbose, "Checksum Value (bytes)", checksum)
295
+ # _logsi.LogBinary(SILevel.Verbose, "Encrypted Blob (unsigned, bytes)", self._EncryptedBlob)
296
+
297
+ # clientKey:str = "7efznlz1HspR5tQftOUdz7cv/L6V5EuXWI596GyiQMist7piBWxfbdAjcpP9/o7WnCl5HdwgI7nY6cQ7ktz3Tsbs4kSG5mbi1QwnYi8IbJ9sdYdd8YxI3Vl6dfuG3JWP"
298
+ # result_b64decode = b64decode(clientKey)
299
+ # result_b64_to_int = b64_to_int(clientKey) # 114863 ...
300
+ # # result_string_to_int = string_to_int(clientKey) # 114863 ...
301
+ # # result_byte_list_to_int = byte_list_to_int(clientKey) # 114863 ...
302
+ # # result_dh_keys_Exchange = self.dh_keys.Exchange(clientKey) # 6243 ...
303
+ # #resultInt = self.dh_keys.Exchange(clientKey) # exception
304
+ # #resultInt = self.dh_keys.shared_secret(b64_to_int(resultBytes)) # exception
305
+ # #result_dh_keys_shared_secret = self.dh_keys.shared_secret(string_to_int(resultBytes)) # 438506 ...
306
+ # #resultInt = self.dh_keys.shared_secret(string_to_int(clientKey)) # exception ...
307
+
308
+ # remote_device_key:int = b64_to_int(clientKey)
309
+ # shared_key:int = 478549389067009439856765960469867849375843534534536997108028584308833390603433510828238916782187762759272369310194601566745030716193055009703822032238289108648174583850368649722797922715533666774901465941959787614689524297840942749
310
+ # shared_key_int_to_bytes = int_to_bytes(shared_key)
311
+ # #shared_key_b64encode = b64encode(shared_key)
312
+ # shared_key_b64encode_int_to_bytes = b64encode(int_to_bytes(shared_key))
313
+ # shared_key_b64encode_utf8 = b64encode(int_to_bytes(shared_key)).decode('utf-8')
314
+
315
+
316
+ # #shared_key:int = self.dh_keys.shared_secret(remote_device_key)
317
+ # b_shared_key:bytes = int_to_bytes(shared_key)
318
+ # base_key:bytes = hashlib.sha1(b_shared_key).digest()[:16]
319
+ # #tmpbytes = b64decode(clientKey)
320
+ # #shared_key:int = byte_list_to_int(tmpbytes)
321
+ # #shared_key:int = b64_to_int(clientKey)
322
+ # #shared_key = self.dh_keys.Exchange(clientKey)
323
+ # _logsi.LogValue(SILevel.Verbose, "Shared Key: %s" % shared_key, shared_key)
324
+ # #baseKey := func() []byte { sum := sha1.Sum(sharedSecret); return sum[:16] }()
325
+ # #b_shared_key:bytes = int_to_bytes(shared_key)
326
+ # #base_key:bytes = hashlib.sha1(b_shared_key).digest()[:16]
327
+ # #encryptionKey := func() []byte { sum := mac.Sum(nil); return sum[:16] }()
328
+ # encryption_key:bytes = hmac.new(base_key, b'encryption', 'sha1').digest()
329
+ # _logsi.LogBinary(SILevel.Verbose, "Encryption Key", encryption_key)
330
+
331
+ # # # create remote device key.
332
+ # # remote_device_key:int = b64_to_int(self.RemotePublicKeyBase64String)
333
+ # # _logsi.LogValue(SILevel.Verbose, "Remote Device Key", remote_device_key)
334
+
335
+ # # # create shared key.
336
+ # # shared_key:int = self.dh_keys.shared_secret(remote_device_key)
337
+ # # _logsi.LogValue(SILevel.Verbose, "Shared Key: %s" % shared_key, shared_key)
338
+
339
+ # # b_shared_key:bytes = int_to_bytes(shared_key)
340
+ # # base_key:bytes = hashlib.sha1(b_shared_key).digest()[:16]
341
+
342
+ # # # create encryption key.
343
+ # # encryption_key:bytes = hmac.new(base_key, b'encryption', 'sha1').digest()
344
+ # # _logsi.LogBinary(SILevel.Verbose, "Encryption Key", encryption_key)
345
+
346
+ # # create new AES cipher, and decrypt the data.
347
+ # ctr_e:dict = Counter.new(128, initial_value=byte_list_to_int(iv))
348
+ # cipher = AES.new(encryption_key[:16], AES.MODE_CTR, counter=ctr_e)
349
+ # self._decrypted_blob = cipher.decrypt(self._EncryptedBlob)
350
+ # _logsi.LogBinary(SILevel.Verbose, "Blob Data, Encrypted base64-encoded bytes", self._decrypted_blob)
351
+ # self._blob = b64decode(self._decrypted_blob)
352
+ # _logsi.LogBinary(SILevel.Verbose, "Blob Data, Encrypted Bytes", self._blob)
353
+
354
+
355
+ # # cipher.NewCTR(bc, iv).XORKeyStream(decrypted, encrypted)
356
+
357
+ # finally:
358
+
359
+ # # trace.
360
+ # _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
@@ -1,11 +1,17 @@
1
1
  import secrets
2
2
 
3
- from .helpers import byte_list_to_int
3
+ from .helpers import byte_list_to_int, int_to_bytes, b64_to_int, int_to_b64str
4
4
 
5
5
  class CryptoDiffieHellman:
6
+ """
7
+ Diffie Hellman cryptography helper class.
8
+
9
+ The following link does a pretty good job of explaining this process:
10
+ https://www.comparitech.com/blog/information-security/diffie-hellman-key-exchange/
11
+ """
6
12
 
7
13
  DH_GENERATOR = 2
8
- DH_PRIME = byte_list_to_int([
14
+ DH_PRIME_MODULUS = byte_list_to_int([
9
15
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2,
10
16
  0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67,
11
17
  0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e,
@@ -16,9 +22,68 @@ class CryptoDiffieHellman:
16
22
 
17
23
 
18
24
  def __init__(self):
19
- self._private_key = secrets.randbits(95 * 8)
20
- self.public_key = pow(CryptoDiffieHellman.DH_GENERATOR, self._private_key, CryptoDiffieHellman.DH_PRIME)
25
+ """
26
+ Initializes a new instance of the class.
27
+ """
28
+ # generate our private key value.
29
+ self._PrivateKey = secrets.randbits(95 * 8)
30
+
31
+ # generate a public key that we can share with a partner.
32
+ self._PublicKey:int = pow(CryptoDiffieHellman.DH_GENERATOR, self._PrivateKey, CryptoDiffieHellman.DH_PRIME_MODULUS)
33
+
34
+
35
+ @property
36
+ def PublicKey(self) -> int:
37
+ """
38
+ Returns our public key in an integer format.
39
+ """
40
+ return self._PublicKey
41
+
42
+ @PublicKey.setter
43
+ def PublicKey(self, value:int):
44
+ """
45
+ Sets the PublicKey property value.
46
+ """
47
+ if isinstance(value, int):
48
+ self._PublicKey = value
49
+
21
50
 
51
+ @property
52
+ def PublicKeyBase64String(self) -> str:
53
+ """
54
+ Returns our public key in a base64 encoded string format.
55
+ """
56
+ return int_to_b64str(self._PublicKey)
57
+
22
58
 
23
- def shared_secret(self, remote_key):
24
- return pow(remote_key, self._private_key, CryptoDiffieHellman.DH_PRIME)
59
+ def SharedSecret(self, remotePublicKey:int):
60
+ """
61
+ Returns a shared secret value that is calculated based upon the specified `remotePublicKey`
62
+ value, our private key value, and a shared Modulus value.
63
+
64
+ Args:
65
+ remotePublicKey (int):
66
+ The Remote clients PublicKey value.
67
+
68
+ The shared secret value can be calculated as:
69
+ SharedSecret = remotePublicKey(base,g) ** PrivateKey(secret,a) % DH_PRIME(modulus,p)
70
+ SharedSecret = g**a mod p
71
+ SharedSecret = base ** secret % modulus
72
+
73
+ - base = remotePublicKey
74
+ - secret = PrivateKey
75
+ - modulus = DH_PRIME
76
+ """
77
+ # calculate the shared secret value based upon the remote's PublicKey, our PrivateKey, and the shared Modulus.
78
+
79
+ # another way to express this formula is:
80
+ # SharedSecret = g**a mod p
81
+
82
+ # another way to express this formula is:
83
+ # SharedSecret = base ** secret % modulus
84
+
85
+ # - base or `g` = remotePublicKey
86
+ # - secret or `a` = PrivateKey
87
+ # - modulus or `p` = DH_PRIME
88
+ return pow(remotePublicKey, self._PrivateKey, CryptoDiffieHellman.DH_PRIME_MODULUS)
89
+
@@ -1,4 +1,5 @@
1
1
  from base64 import b64decode, b64encode
2
+ import binascii
2
3
 
3
4
 
4
5
  def write_int(i: int, out: bytearray):
@@ -14,17 +15,43 @@ def write_bytes(b: bytes, out: bytearray):
14
15
  out.extend(b)
15
16
 
16
17
 
17
- def byte_list_to_int(byte_list):
18
+ def byte_list_to_int(byte_list) -> int:
19
+ """
20
+ Converts a list of bytes to a big integer.
21
+ """
18
22
  return int.from_bytes(bytes(byte_list), 'big')
19
23
 
20
24
 
21
- def int_to_bytes(i: int):
22
- return i.to_bytes((i.bit_length() + 7) // 8, byteorder='big')
25
+ def int_to_bytes(value: int) -> bytes:
26
+ """
27
+ Converts a big integer to bytes.
28
+ """
29
+ return value.to_bytes((value.bit_length() + 7) // 8, byteorder='big') or b'\0'
23
30
 
24
31
 
25
- def int_to_b64str(i: int):
26
- return b64encode(int_to_bytes(i)).decode('ascii')
32
+ def int_to_b64str(value: int) -> str:
33
+ """
34
+ Converts a big integer to a base64-encoded string value.
35
+ """
36
+ return b64encode(int_to_bytes(value)).decode('ascii')
27
37
 
28
38
 
29
- def b64_to_int(b64: str):
30
- return int.from_bytes(b64decode(b64), 'big')
39
+ def b64_to_int(value: str):
40
+ """
41
+ Converts a base64-encoded string or byte array to a big integer.
42
+ """
43
+ return int.from_bytes(b64decode(value), 'big')
44
+
45
+
46
+ def string_to_int(value):
47
+ """
48
+ Converts a string to a big integer.
49
+ """
50
+ return int(binascii.hexlify(value), 16)
51
+
52
+
53
+ def int_of_string(value):
54
+ """
55
+ Converts a string to a big integer.
56
+ """
57
+ return int(binascii.hexlify(value), 16)