|
| 1 | +# -\*- coding: utf-8 -\*- |
| 2 | + |
| 3 | +''' This is mainly for string obfuscation. ''' |
| 4 | + |
| 5 | +import base64 |
| 6 | +import codecs |
| 7 | +import hashlib |
| 8 | +from seleniumbase.config import settings |
| 9 | + |
| 10 | + |
| 11 | +def str_xor(string, key): |
| 12 | + if len(key) < 1: |
| 13 | + raise Exception("2nd arg of str_xor() must be a string of length > 0!") |
| 14 | + if len(string) > len(key): |
| 15 | + difference = len(string) - len(key) |
| 16 | + key = key + ( |
| 17 | + ((difference / len(key)) * key) + key) |
| 18 | + result = None |
| 19 | + try: |
| 20 | + result = "".join( |
| 21 | + [chr(ord(c1) ^ ord(c2)) for (c1, c2) in zip(string, key)]) |
| 22 | + except: |
| 23 | + string = string.decode('utf-8') |
| 24 | + result = "".join( |
| 25 | + [chr(ord(c1) ^ ord(c2)) for (c1, c2) in zip(string, key)]) |
| 26 | + return result |
| 27 | + |
| 28 | + |
| 29 | +def is_obfuscated(string): |
| 30 | + # Based on settings, determines if a string has already been obfuscated. |
| 31 | + # Obfuscated strings have a common predefined start token and end token. |
| 32 | + start_token = settings.OBFUSCATION_START_TOKEN |
| 33 | + end_token = settings.OBFUSCATION_END_TOKEN |
| 34 | + return (string.startswith(start_token) and string.endswith(end_token)) |
| 35 | + |
| 36 | + |
| 37 | +def shuffle_string(string): |
| 38 | + if len(string) < 2: |
| 39 | + return string |
| 40 | + return (string[1::2] + string[::2]) |
| 41 | + |
| 42 | + |
| 43 | +def reverse_shuffle_string(string): |
| 44 | + if len(string) < 2: |
| 45 | + return string |
| 46 | + new_string = "" |
| 47 | + odd = (len(string) % 2 == 1) |
| 48 | + part1 = string[:int(len(string)/2):1] |
| 49 | + part2 = string[int(len(string)/2)::1] |
| 50 | + for c in range(len(part1)): |
| 51 | + new_string += part2[c] |
| 52 | + new_string += part1[c] |
| 53 | + if odd: |
| 54 | + new_string += part2[-1] |
| 55 | + return new_string |
| 56 | + |
| 57 | + |
| 58 | +def blend_strings(string1, string2): |
| 59 | + smallest_length = min(len(string1), len(string2)) |
| 60 | + new_string = "" |
| 61 | + for c in range(smallest_length): |
| 62 | + new_string += string1[c] |
| 63 | + new_string += string2[c] |
| 64 | + if len(string1) > len(string2): |
| 65 | + new_string += string1[smallest_length:] |
| 66 | + elif len(string2) > len(string1): |
| 67 | + new_string += string2[smallest_length:] |
| 68 | + else: |
| 69 | + # Equal length strings |
| 70 | + pass |
| 71 | + return new_string |
| 72 | + |
| 73 | + |
| 74 | +def rotate(l, n): |
| 75 | + return l[n:] + l[:n] |
| 76 | + |
| 77 | + |
| 78 | +def ord_string_sum(string): |
| 79 | + count = 0 |
| 80 | + try: |
| 81 | + for c in string: |
| 82 | + count += ord(c) |
| 83 | + except: |
| 84 | + string = string.decode('utf-8') |
| 85 | + for c in string: |
| 86 | + count += ord(c) |
| 87 | + return count |
| 88 | + |
| 89 | + |
| 90 | +def decrypt(string): |
| 91 | + # Password/String obfuscation/de-obfuscation |
| 92 | + # Used for both encryption and decryption |
| 93 | + # If you update the algorithm, you must re-encrypt all encrypted passwords! |
| 94 | + encryption_key = settings.ENCRYPTION_KEY |
| 95 | + start_token = settings.OBFUSCATION_START_TOKEN |
| 96 | + end_token = settings.OBFUSCATION_END_TOKEN |
| 97 | + already_encrypted = False |
| 98 | + if is_obfuscated(string): |
| 99 | + already_encrypted = True |
| 100 | + string = string[len(start_token):-len(end_token)] |
| 101 | + string = base64.b64decode(codecs.encode(string)) |
| 102 | + # Obfuscate the key used for string obfuscation |
| 103 | + hd1 = hashlib.sha256(str(encryption_key).encode('utf-8')).hexdigest() |
| 104 | + hd2 = hashlib.sha256(str(encryption_key[::-1]).encode('utf-8')).hexdigest() |
| 105 | + b64_key = base64.b64encode(codecs.encode(encryption_key * 8)) |
| 106 | + xor_key = "".join([chr(ord(str(c3)) - int(c1, 16) - int(c2, 16)) for ( |
| 107 | + c1, c2, c3) in zip(hd1, hd2, b64_key.decode("utf-8"))]) |
| 108 | + xor_key = blend_strings(xor_key, encryption_key) |
| 109 | + if len(xor_key) % 7 == 0: |
| 110 | + xor_key = xor_key + encryption_key[-1] |
| 111 | + xor_key = shuffle_string((xor_key * 8)[::7]) |
| 112 | + # Use the str_xor method for the main string obfuscation / de-obfuscation |
| 113 | + if not already_encrypted: |
| 114 | + if len(string) > 0: |
| 115 | + rem1 = (ord_string_sum(string)) % 3 |
| 116 | + rem2 = (ord_string_sum(string)) % 4 |
| 117 | + rem3 = (ord_string_sum(string)) % 2 |
| 118 | + rem4 = (len(string) + ord_string_sum(string)) % 2 |
| 119 | + if len(string) % 2 != 0: |
| 120 | + if rem3 == 1: |
| 121 | + string = (chr(ord(string[-1])-5-rem1) + string + |
| 122 | + chr(ord(string[-1])-13-rem1)) |
| 123 | + else: |
| 124 | + string = (chr(ord(string[-1])-11-rem1) + string + |
| 125 | + chr(ord(string[-1])-23-rem1)) |
| 126 | + elif len(string) > 1: |
| 127 | + if rem4 == 1: |
| 128 | + string = (chr(ord(string[0])-19+rem2) + string + |
| 129 | + chr(ord(string[0])-7-rem2)) |
| 130 | + else: |
| 131 | + string = (chr(ord(string[0])-26+rem2) + string + |
| 132 | + chr(ord(string[0])-12-rem2)) |
| 133 | + rem5 = (len(string) + ord_string_sum(string)) % 23 |
| 134 | + string = rotate(string, rem5) |
| 135 | + result = str_xor(shuffle_string(string)[::-1], xor_key) |
| 136 | + rem6 = (len(result) + ord_string_sum(result)) % 17 |
| 137 | + result = rotate(result, rem6) |
| 138 | + else: |
| 139 | + rem6 = (len(string) + ord_string_sum(string)) % 17 |
| 140 | + string = rotate(string, -rem6) |
| 141 | + result = reverse_shuffle_string(str_xor(string, xor_key)[::-1]) |
| 142 | + if len(result) > 2: |
| 143 | + rem5 = (len(result) + ord_string_sum(result)) % 23 |
| 144 | + result = rotate(result, -rem5) |
| 145 | + result = result[1:-1] |
| 146 | + # Finalize encryption of non-encrypted string |
| 147 | + if not already_encrypted: |
| 148 | + result = base64.b64encode(codecs.encode(result)) |
| 149 | + result = start_token + result.decode("utf-8") + end_token |
| 150 | + return result |
0 commit comments