Skip to content

Commit 11cd284

Browse files
committed
Migration to python3
In the commit you can find many converts from string to binary
1 parent 1155320 commit 11cd284

16 files changed

+289
-254
lines changed

docs/protocols/SAPRouter.ipynb

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
"outputs": [],
130130
"source": [
131131
"router_string = [SAPRouterRouteHop(hostname=\"8.8.8.8\", port=3299),\n",
132-
" SAPRouterRouteHop(hostname=\"10.0.0.1\", port=3200, password=\"S3cr3t\")]\n",
132+
" SAPRouterRouteHop(hostname=\"127.0.0.1\", port=3200, password=\"S3cr3t\")]\n",
133133
"router_string_lens = map(len, map(str, router_string))\n",
134134
"p = SAPRouter(type=SAPRouter.SAPROUTER_ROUTE,\n",
135135
" route_entries=len(router_string),\n",

pysap/SAPCredv2.py

+40-14
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
log_cred = logging.getLogger("pysap.cred")
4949

5050

51-
cred_key_fmt = "240657rsga&/%srwthgrtawe45hhtrtrsr35467b2dx3456j67mv67f89656f75"
51+
cred_key_fmt = b"240657rsga&/%srwthgrtawe45hhtrtrsr35467b2dx3456j67mv67f89656f75"
5252
"""Fixed key embedded in CommonCryptoLib for encrypted credentials"""
5353

5454

@@ -170,14 +170,19 @@ def lps_type_str(self):
170170
@property
171171
def cipher_format_version(self):
172172
cipher = self.cipher.val_readable
173-
if len(cipher) >= 36 and ord(cipher[0]) in [0, 1]:
174-
return ord(cipher[0])
173+
if len(cipher) >= 36:
174+
first_byte = cipher[0]
175+
if isinstance(first_byte, int):
176+
return first_byte if first_byte in [0, 1] else 0
177+
elif isinstance(first_byte, str):
178+
return ord(first_byte) if ord(first_byte) in [0, 1] else 0
175179
return 0
176180

177181
@property
178182
def cipher_algorithm(self):
179183
if self.cipher_format_version == 1:
180-
return ord(self.cipher.val_readable[1])
184+
second_byte = self.cipher.val_readable[1]
185+
return ord(second_byte) if isinstance(second_byte, str) else second_byte
181186
return 0
182187

183188
def decrypt(self, username):
@@ -213,7 +218,7 @@ def decrypt_simple(self, username):
213218
# Construct the key using the key format and the username
214219
key = (cred_key_fmt % username)[:24]
215220
# Set empty IV
216-
iv = "\x00" * 8
221+
iv = b"\x00" * 8
217222

218223
# Decrypt the cipher text with the derived key and IV
219224
decryptor = Cipher(algorithms.TripleDES(key), modes.CBC(iv), backend=default_backend()).decryptor()
@@ -225,12 +230,15 @@ def xor(self, string, start):
225230
"""XOR a given string using a fixed key and a starting number."""
226231
key = 0x15a4e35
227232
x = start
228-
y = ""
233+
result = bytearray()
229234
for c in string:
230235
x *= key
231236
x += 1
232-
y += chr(ord(c) ^ (x & 0xff))
233-
return y
237+
if isinstance(c, int):
238+
result.append(c ^ (x & 0xff))
239+
else:
240+
result.append(ord(c) ^ (x & 0xff))
241+
return bytes(result)
234242

235243
def derive_key(self, key, blob, header, username):
236244
"""Derive a key using SAP's algorithm. The key is derived using SHA256 and xor from an
@@ -240,10 +248,22 @@ def derive_key(self, key, blob, header, username):
240248
digest.update(key)
241249
digest.update(blob[0:4])
242250
digest.update(header.salt)
243-
digest.update(self.xor(username, ord(header.salt[0])))
244-
digest.update("" * 0x20)
251+
252+
# Handle both string and integer types for salt[0]
253+
salt_value = header.salt[0]
254+
if isinstance(salt_value, str):
255+
salt_value = ord(salt_value)
256+
257+
digest.update(self.xor(username, salt_value))
258+
digest.update(b"" * 0x20)
245259
hashed = digest.finalize()
246-
derived_key = self.xor(hashed, ord(header.salt[1]))
260+
261+
# Handle both string and integer types for salt[1]
262+
salt_value = header.salt[1]
263+
if isinstance(salt_value, str):
264+
salt_value = ord(salt_value)
265+
266+
derived_key = self.xor(hashed, salt_value)
247267

248268
# Validate and select proper algorithm
249269
if header.algorithm == CIPHER_ALGORITHM_3DES:
@@ -340,7 +360,8 @@ def pse_file_path(self):
340360

341361
@property
342362
def lps_type(self):
343-
return ord(self.cipher.val_readable[1])
363+
value = self.cipher.val_readable[1]
364+
return value if isinstance(value, int) else ord(value)
344365

345366
@property
346367
def lps_type_str(self):
@@ -352,7 +373,8 @@ def lps_type_str(self):
352373

353374
@property
354375
def cipher_format_version(self):
355-
return ord(self.cipher.val_readable[0])
376+
value = self.cipher.val_readable[0]
377+
return value if isinstance(value, int) else ord(value)
356378

357379
@property
358380
def cipher_algorithm(self):
@@ -377,7 +399,11 @@ def decrypt(self, username=None):
377399
plain = cipher.decrypt()
378400

379401
# Get the pin from the raw data
380-
plain_size = ord(plain[0])
402+
if isinstance(plain[0], int):
403+
plain_size = plain[0]
404+
else:
405+
plain_size = ord(plain[0])
406+
381407
pin = plain[plain_size + 1:]
382408

383409
# Create a plain credential container

pysap/SAPLPS.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
log_lps = logging.getLogger("pysap.lps")
3535

3636

37-
cred_key_lps_fallback = "\xe7\x6a\xd2\xce\x4b\xa7\xc7\x9e\xf9\x79\x5f\xa8\x2e\x6e\xaa\x1d\x76\x02\x2e\xcd\xd7\x74\x38\x51"
37+
cred_key_lps_fallback = b"\xe7\x6a\xd2\xce\x4b\xa7\xc7\x9e\xf9\x79\x5f\xa8\x2e\x6e\xaa\x1d\x76\x02\x2e\xcd\xd7\x74\x38\x51"
3838
"""Fixed key embedded in CommonCryptoLib for encrypted credentials using LPS in fallback mode"""
3939

4040

@@ -110,7 +110,7 @@ def decrypt(self):
110110
raise SAPLPSDecryptionError("Invalid LPS decryption method")
111111

112112
# Decrypt the cipher text with the encryption key
113-
iv = "\x00" * 16
113+
iv = b"\x00" * 16
114114
decryptor = Cipher(algorithms.AES(encryption_key), modes.CBC(iv)).decryptor()
115115
plain = decryptor.update(self.encrypted_data) + decryptor.finalize()
116116

@@ -148,7 +148,7 @@ def decrypt_encryption_key_fallback(self):
148148
hmac.update(self.context)
149149
default_key = hmac.finalize()[:16]
150150

151-
iv = "\x00" * 16
151+
iv = b"\x00" * 16
152152
decryptor = Cipher(algorithms.AES(default_key), modes.CBC(iv)).decryptor()
153153
encryption_key = decryptor.update(self.encrypted_key) + decryptor.finalize()
154154

pysap/SAPNI.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def recv(self):
123123
log_sapni.debug("Received 4 bytes NI header, to receive %d bytes data", nilength)
124124

125125
# Receive the whole NI packet (length+payload)
126-
nidata = ''
126+
nidata = b''
127127
while len(nidata) < nilength + 4:
128128
nidata += self.ins.recv(nilength - len(nidata) + 4)
129129
if len(nidata) == 0:

pysap/SAPPSE.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -255,11 +255,12 @@ def decrypt(self, pin):
255255
"""Decrypts a PSE file given a provided PIN. Calls the respective decryption function
256256
based on the PSE version.
257257
"""
258-
258+
# Convert the pin to bytes
259+
pin_bytes = pin.encode('utf-8') if isinstance(pin, str) else pin
259260
if self.version == 2:
260-
return self.decrypt_non_lps(pin)
261+
return self.decrypt_non_lps(pin_bytes)
261262
elif self.version == 256:
262-
return self.decrypt_lps(pin)
263+
return self.decrypt_lps(pin_bytes)
263264
else:
264265
raise ValueError("Unsupported or invalid PSE version")
265266

pysap/SAPRouter.py

+17-11
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,17 @@ def from_string(cls, route_string):
160160
(/H/host/S/service/P/pass)*
161161
162162
:param route_string: route string
163-
:type route_string: C{string}
163+
:type route_string: C{string} or C{bytes}
164164
165165
:return: route hops in the route string
166166
:rtype: ``list`` of :class:`SAPRouterRouteHop`
167167
"""
168168
result = []
169+
170+
# Convert bytes to string if necessary
171+
if isinstance(route_string, bytes):
172+
route_string = route_string.decode('utf-8')
173+
169174
for route_hop in [x.groupdict() for x in cls.regex.finditer(route_string)]:
170175
result.append(cls(hostname=route_hop["hostname"],
171176
port=route_hop["port"],
@@ -182,14 +187,15 @@ def from_hops(cls, route_hops):
182187
:return: route string
183188
:rtype: C{string}
184189
"""
185-
result = ""
186-
for route_hop in route_hops:
187-
result += "/H/{}".format(route_hop.hostname)
188-
if route_hop.port:
189-
result += "/S/{}".format(route_hop.port)
190-
if route_hop.password:
191-
result += "/W/{}".format(route_hop.password)
192-
return result
190+
route_string = ""
191+
for hop in route_hops:
192+
route_string += "/H/" + hop.hostname.decode('utf-8') if isinstance(hop.hostname, bytes) else hop.hostname
193+
if hop.port:
194+
route_string += "/S/" + hop.port.decode('utf-8') if isinstance(hop.port, bytes) else hop.port
195+
if hop.password:
196+
route_string += "/W/" + hop.password.decode('utf-8') if isinstance(hop.password,
197+
bytes) else hop.password
198+
return route_string
193199

194200

195201
class SAPRouterInfoClient(PacketNoPadded):
@@ -269,7 +275,7 @@ class SAPRouterError(PacketNoPadded):
269275
StrNullField("XXX6", ""),
270276
StrNullField("XXX7", ""),
271277
StrNullField("XXX8", ""),
272-
StrNullField("eyecatcher", "*ERR*"),
278+
StrNullField("eyecatcher2", "*ERR*"),
273279
]
274280

275281
time_format = "%a %b %d %H:%M:%S %Y"
@@ -452,7 +458,7 @@ class SAPRouter(Packet):
452458
# Cancel Route fields
453459
ConditionalField(FieldLenField("adm_client_count", None, count_of="adm_client_ids", fmt="H"), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [6]),
454460
# Trace Connection fields
455-
ConditionalField(FieldLenField("adm_client_count", None, count_of="adm_client_ids", fmt="I"), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [12, 13]),
461+
ConditionalField(FieldLenField("adm_client_count2", None, count_of="adm_client_ids", fmt="I"), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [12, 13]),
456462

457463
# Cancel Route or Trace Connection fields
458464
ConditionalField(FieldListField("adm_client_ids", [0x00], IntField("", 0), count_from=lambda pkt:pkt.adm_client_count), lambda pkt:router_is_admin(pkt) and pkt.adm_command in [6, 12, 13]),

0 commit comments

Comments
 (0)