├── .github └── workflows │ └── docker-ghcr.yml ├── Dockerfile ├── LICENSE ├── README.md ├── config.py ├── docker-compose.yml ├── mtprotoproxy.py ├── mtproxy.py └── pyaes ├── __init__.py ├── aes.py ├── blockfeeder.py └── util.py /.github/workflows/docker-ghcr.yml: -------------------------------------------------------------------------------- 1 | name: Docker Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: 'Version number' 8 | required: true 9 | default: 'latest' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v2 18 | 19 | - name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v2 21 | 22 | - name: Cache Docker layers 23 | uses: actions/cache@v3 24 | with: 25 | path: /tmp/.buildx-cache 26 | key: ${{ runner.os }}-buildx-${{ github.sha }} 27 | restore-keys: | 28 | ${{ runner.os }}-buildx- 29 | 30 | - name: Log in to GitHub Docker Registry 31 | uses: docker/login-action@v2 32 | with: 33 | registry: ghcr.io 34 | username: ${{ github.actor }} 35 | password: ${{ secrets.GITHUB_TOKEN }} 36 | 37 | - name: Set version from input 38 | run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV 39 | 40 | - name: Build and push Docker image 41 | run: | 42 | docker buildx build --push \ 43 | --platform linux/amd64,linux/arm64/v8,linux/arm/v7 \ 44 | --tag ghcr.io/${{ github.repository }}:${{ env.VERSION }} \ 45 | --tag ghcr.io/${{ github.repository }}:latest . 46 | 47 | - name: Create GitHub release 48 | uses: actions/create-release@v1 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | with: 52 | tag_name: ${{ env.VERSION }} 53 | release_name: Release ${{ env.VERSION }} 54 | draft: false 55 | prerelease: false 56 | overwrite: true 57 | body: | 58 | ## Changes 59 | - See the [commit history](https://github.com/${{ github.repository }}/commits/${{ env.VERSION }}) for more details. 60 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN apt-get update && apt-get install --no-install-recommends -y python3 python3-uvloop python3-cryptography python3-socks libcap2-bin ca-certificates && rm -rf /var/lib/apt/lists/* 4 | RUN setcap cap_net_bind_service=+ep /usr/bin/python3.10 5 | 6 | RUN useradd tgproxy -u 10000 7 | 8 | USER tgproxy 9 | 10 | WORKDIR /home/tgproxy/ 11 | 12 | COPY --chown=tgproxy mtprotoproxy.py config.py /home/tgproxy/ 13 | 14 | CMD ["python3", "mtprotoproxy.py"] 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018, Alexander Bersenev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ✨Python版TG代理 2 | 3 | >适配/amd64/arm64/armv7架构 4 | > 5 | >支持ipv4和ipv6 6 | > 7 | >默认开启流量混淆和TLS加密 8 | > 9 | >支持TG频道推广(需要TG老号) 10 | 11 | 12 | ## 🟢Docker部署 13 | 14 | > 自行安装`git`和`docker` 15 | 16 | ### 下载项目 17 | 18 | ``` 19 | git clone https://github.com/admin8800/mtprotoproxy.git 20 | ``` 21 | ``` 22 | cd mtprotoproxy 23 | ``` 24 | #### 修改`config.py`文件中的配置 25 | 26 | #### 32位数密钥可在[这个网站在线生成](https://www.lzltool.com/Tools/RandomHex) 27 | 28 | #### 启动运行 29 | 30 | ``` 31 | docker compose up -d 32 | ``` 33 | 34 | #### 查看链接和日志 35 | 36 | ``` 37 | docker logs mtp 38 | ``` 39 | 40 | - 重启 `docker restart mtp` 41 | 42 | - 停止 `docker stop mtp` 43 | 44 | - 删除 `docker rm -f mtp` 45 | 46 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # 连接端口 2 | PORT = 8443 3 | 4 | # 密钥(32个十六进制字符) 5 | USERS = { 6 | "TG链接": "bc6dddb180509c5b18cb51d17d849455", 7 | #"TG链接二": "fa33e8ed3523ce760c241129c66153b6", 8 | } 9 | 10 | MODES = { 11 | # 经典模式,容易被墙 12 | "classic": False, 13 | 14 | # 流量混淆 15 | "secure": True, 16 | 17 | # TLS加密 18 | "tls": True 19 | } 20 | 21 | # 伪装域名 22 | TLS_DOMAIN = "www.swift.com" 23 | 24 | #广告标签,可从 @MTProxybot 获取 25 | #AD_TAG = "3c09c680b76ee91a4c25ad51f742267d" 26 | 27 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mtprotoproxy: 3 | image: ghcr.io/admin8800/mtprotoproxy:latest 4 | container_name: mtp 5 | restart: unless-stopped 6 | network_mode: "host" 7 | volumes: 8 | - ./config.py:/home/tgproxy/config.py 9 | - ./mtprotoproxy.py:/home/tgproxy/mtprotoproxy.py 10 | - /etc/localtime:/etc/localtime:ro 11 | logging: 12 | driver: "json-file" 13 | options: 14 | max-file: "5" 15 | max-size: "10m" 16 | # mem_limit: 2048m 17 | -------------------------------------------------------------------------------- /mtprotoproxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import asyncio 4 | import socket 5 | import urllib.parse 6 | import urllib.request 7 | import collections 8 | import time 9 | import datetime 10 | import hmac 11 | import base64 12 | import hashlib 13 | import random 14 | import binascii 15 | import sys 16 | import re 17 | import runpy 18 | import signal 19 | import os 20 | import stat 21 | import traceback 22 | 23 | 24 | TG_DATACENTER_PORT = 443 25 | 26 | TG_DATACENTERS_V4 = [ 27 | "149.154.175.50", "149.154.167.51", "149.154.175.100", 28 | "149.154.167.91", "149.154.171.5" 29 | ] 30 | 31 | TG_DATACENTERS_V6 = [ 32 | "2001:b28:f23d:f001::a", "2001:67c:04e8:f002::a", "2001:b28:f23d:f003::a", 33 | "2001:67c:04e8:f004::a", "2001:b28:f23f:f005::a" 34 | ] 35 | 36 | # This list will be updated in the runtime 37 | TG_MIDDLE_PROXIES_V4 = { 38 | 1: [("149.154.175.50", 8888)], -1: [("149.154.175.50", 8888)], 39 | 2: [("149.154.161.144", 8888)], -2: [("149.154.161.144", 8888)], 40 | 3: [("149.154.175.100", 8888)], -3: [("149.154.175.100", 8888)], 41 | 4: [("91.108.4.136", 8888)], -4: [("149.154.165.109", 8888)], 42 | 5: [("91.108.56.181", 8888)], -5: [("91.108.56.181", 8888)] 43 | } 44 | 45 | TG_MIDDLE_PROXIES_V6 = { 46 | 1: [("2001:b28:f23d:f001::d", 8888)], -1: [("2001:b28:f23d:f001::d", 8888)], 47 | 2: [("2001:67c:04e8:f002::d", 80)], -2: [("2001:67c:04e8:f002::d", 80)], 48 | 3: [("2001:b28:f23d:f003::d", 8888)], -3: [("2001:b28:f23d:f003::d", 8888)], 49 | 4: [("2001:67c:04e8:f004::d", 8888)], -4: [("2001:67c:04e8:f004::d", 8888)], 50 | 5: [("2001:b28:f23f:f005::d", 8888)], -5: [("2001:67c:04e8:f004::d", 8888)] 51 | } 52 | 53 | PROXY_SECRET = bytes.fromhex( 54 | "c4f9faca9678e6bb48ad6c7e2ce5c0d24430645d554addeb55419e034da62721" + 55 | "d046eaab6e52ab14a95a443ecfb3463e79a05a66612adf9caeda8be9a80da698" + 56 | "6fb0a6ff387af84d88ef3a6413713e5c3377f6e1a3d47d99f5e0c56eece8f05c" + 57 | "54c490b079e31bef82ff0ee8f2b0a32756d249c5f21269816cb7061b265db212" 58 | ) 59 | 60 | SKIP_LEN = 8 61 | PREKEY_LEN = 32 62 | KEY_LEN = 32 63 | IV_LEN = 16 64 | HANDSHAKE_LEN = 64 65 | TLS_HANDSHAKE_LEN = 1 + 2 + 2 + 512 66 | PROTO_TAG_POS = 56 67 | DC_IDX_POS = 60 68 | 69 | MIN_CERT_LEN = 1024 70 | 71 | PROTO_TAG_ABRIDGED = b"\xef\xef\xef\xef" 72 | PROTO_TAG_INTERMEDIATE = b"\xee\xee\xee\xee" 73 | PROTO_TAG_SECURE = b"\xdd\xdd\xdd\xdd" 74 | 75 | CBC_PADDING = 16 76 | PADDING_FILLER = b"\x04\x00\x00\x00" 77 | 78 | MIN_MSG_LEN = 12 79 | MAX_MSG_LEN = 2 ** 24 80 | 81 | STAT_DURATION_BUCKETS = [0.1, 0.5, 1, 2, 5, 15, 60, 300, 600, 1800, 2**31 - 1] 82 | 83 | my_ip_info = {"ipv4": None, "ipv6": None} 84 | used_handshakes = collections.OrderedDict() 85 | client_ips = collections.OrderedDict() 86 | last_client_ips = {} 87 | disable_middle_proxy = False 88 | is_time_skewed = False 89 | fake_cert_len = random.randrange(1024, 4096) 90 | mask_host_cached_ip = None 91 | last_clients_with_time_skew = {} 92 | last_clients_with_same_handshake = collections.Counter() 93 | proxy_start_time = 0 94 | proxy_links = [] 95 | 96 | stats = collections.Counter() 97 | user_stats = collections.defaultdict(collections.Counter) 98 | 99 | config = {} 100 | 101 | 102 | def init_config(): 103 | global config 104 | # we use conf_dict to protect the original config from exceptions when reloading 105 | if len(sys.argv) < 2: 106 | conf_dict = runpy.run_module("config") 107 | elif len(sys.argv) == 2: 108 | # launch with own config 109 | conf_dict = runpy.run_path(sys.argv[1]) 110 | else: 111 | # undocumented way of launching 112 | conf_dict = {} 113 | conf_dict["PORT"] = int(sys.argv[1]) 114 | secrets = sys.argv[2].split(",") 115 | conf_dict["USERS"] = {"user%d" % i: secrets[i].zfill(32) for i in range(len(secrets))} 116 | conf_dict["MODES"] = {"classic": False, "secure": True, "tls": True} 117 | if len(sys.argv) > 3: 118 | conf_dict["AD_TAG"] = sys.argv[3] 119 | if len(sys.argv) > 4: 120 | conf_dict["TLS_DOMAIN"] = sys.argv[4] 121 | conf_dict["MODES"] = {"classic": False, "secure": False, "tls": True} 122 | 123 | conf_dict = {k: v for k, v in conf_dict.items() if k.isupper()} 124 | 125 | conf_dict.setdefault("PORT", 3256) 126 | conf_dict.setdefault("USERS", {"tg": "00000000000000000000000000000000"}) 127 | conf_dict["AD_TAG"] = bytes.fromhex(conf_dict.get("AD_TAG", "")) 128 | 129 | for user, secret in conf_dict["USERS"].items(): 130 | if not re.fullmatch("[0-9a-fA-F]{32}", secret): 131 | fixed_secret = re.sub(r"[^0-9a-fA-F]", "", secret).zfill(32)[:32] 132 | 133 | print_err("用户密钥不正确 %s, 应该是 32 个十六进制字符,得到 %s. " % (user, secret)) 134 | print_err("将其更改为 %s" % fixed_secret) 135 | 136 | conf_dict["USERS"][user] = fixed_secret 137 | 138 | # load advanced settings 139 | 140 | # use middle proxy, necessary to show ad 141 | conf_dict.setdefault("USE_MIDDLE_PROXY", len(conf_dict["AD_TAG"]) == 16) 142 | 143 | # if IPv6 avaliable, use it by default 144 | conf_dict.setdefault("PREFER_IPV6", socket.has_ipv6) 145 | 146 | # disables tg->client trafic reencryption, faster but less secure 147 | conf_dict.setdefault("FAST_MODE", True) 148 | 149 | # enables some working modes 150 | modes = conf_dict.get("MODES", {}) 151 | 152 | if "MODES" not in conf_dict: 153 | modes.setdefault("classic", True) 154 | modes.setdefault("secure", True) 155 | modes.setdefault("tls", True) 156 | else: 157 | modes.setdefault("classic", False) 158 | modes.setdefault("secure", False) 159 | modes.setdefault("tls", False) 160 | 161 | legacy_warning = False 162 | if "SECURE_ONLY" in conf_dict: 163 | legacy_warning = True 164 | modes["classic"] = not bool(conf_dict["SECURE_ONLY"]) 165 | 166 | if "TLS_ONLY" in conf_dict: 167 | legacy_warning = True 168 | if conf_dict["TLS_ONLY"]: 169 | modes["classic"] = False 170 | modes["secure"] = False 171 | 172 | if not modes["classic"] and not modes["secure"] and not modes["tls"]: 173 | print_err("未启用任何已知模式,正在启用 tls-only 模式") 174 | modes["tls"] = True 175 | 176 | if legacy_warning: 177 | print_err("Legacy options SECURE_ONLY or TLS_ONLY detected") 178 | print_err("Please use MODES in your config instead:") 179 | print_err("MODES = {") 180 | print_err(' "classic": %s,' % modes["classic"]) 181 | print_err(' "secure": %s,' % modes["secure"]) 182 | print_err(' "tls": %s' % modes["tls"]) 183 | print_err("}") 184 | 185 | conf_dict["MODES"] = modes 186 | 187 | # accept incoming connections only with proxy protocol v1/v2, useful for nginx and haproxy 188 | conf_dict.setdefault("PROXY_PROTOCOL", False) 189 | 190 | # set the tls domain for the proxy, has an influence only on starting message 191 | conf_dict.setdefault("TLS_DOMAIN", "www.swift.com") 192 | 193 | # enable proxying bad clients to some host 194 | conf_dict.setdefault("MASK", True) 195 | 196 | # the next host to forward bad clients 197 | conf_dict.setdefault("MASK_HOST", conf_dict["TLS_DOMAIN"]) 198 | 199 | # set the home domain for the proxy, has an influence only on the log message 200 | conf_dict.setdefault("MY_DOMAIN", False) 201 | 202 | # the next host's port to forward bad clients 203 | conf_dict.setdefault("MASK_PORT", 443) 204 | 205 | # use upstream SOCKS5 proxy 206 | conf_dict.setdefault("SOCKS5_HOST", None) 207 | conf_dict.setdefault("SOCKS5_PORT", None) 208 | conf_dict.setdefault("SOCKS5_USER", None) 209 | conf_dict.setdefault("SOCKS5_PASS", None) 210 | 211 | if conf_dict["SOCKS5_HOST"] and conf_dict["SOCKS5_PORT"]: 212 | # Disable the middle proxy if using socks, they are not compatible 213 | conf_dict["USE_MIDDLE_PROXY"] = False 214 | 215 | # user tcp connection limits, the mapping from name to the integer limit 216 | # one client can create many tcp connections, up to 8 217 | conf_dict.setdefault("USER_MAX_TCP_CONNS", {}) 218 | 219 | # expiration date for users in format of day/month/year 220 | conf_dict.setdefault("USER_EXPIRATIONS", {}) 221 | for user in conf_dict["USER_EXPIRATIONS"]: 222 | expiration = datetime.datetime.strptime(conf_dict["USER_EXPIRATIONS"][user], "%d/%m/%Y") 223 | conf_dict["USER_EXPIRATIONS"][user] = expiration 224 | 225 | # the data quota for user 226 | conf_dict.setdefault("USER_DATA_QUOTA", {}) 227 | 228 | # length of used handshake randoms for active fingerprinting protection, zero to disable 229 | conf_dict.setdefault("REPLAY_CHECK_LEN", 65536) 230 | 231 | # accept clients with bad clocks. This reduces the protection against replay attacks 232 | conf_dict.setdefault("IGNORE_TIME_SKEW", False) 233 | 234 | # length of last client ip addresses for logging 235 | conf_dict.setdefault("CLIENT_IPS_LEN", 131072) 236 | 237 | # delay in seconds between stats printing 238 | conf_dict.setdefault("STATS_PRINT_PERIOD", 600) 239 | 240 | # delay in seconds between middle proxy info updates 241 | conf_dict.setdefault("PROXY_INFO_UPDATE_PERIOD", 24*60*60) 242 | 243 | # delay in seconds between time getting, zero means disabled 244 | conf_dict.setdefault("GET_TIME_PERIOD", 10*60) 245 | 246 | # delay in seconds between getting the length of certificate on the mask host 247 | conf_dict.setdefault("GET_CERT_LEN_PERIOD", random.randrange(4*60*60, 6*60*60)) 248 | 249 | # max socket buffer size to the client direction, the more the faster, but more RAM hungry 250 | # can be the tuple (low, users_margin, high) for the adaptive case. If no much users, use high 251 | conf_dict.setdefault("TO_CLT_BUFSIZE", (16384, 100, 131072)) 252 | 253 | # max socket buffer size to the telegram servers direction, also can be the tuple 254 | conf_dict.setdefault("TO_TG_BUFSIZE", 65536) 255 | 256 | # keepalive period for clients in secs 257 | conf_dict.setdefault("CLIENT_KEEPALIVE", 10*60) 258 | 259 | # drop client after this timeout if the handshake fail 260 | conf_dict.setdefault("CLIENT_HANDSHAKE_TIMEOUT", random.randrange(5, 15)) 261 | 262 | # if client doesn't confirm data for this number of seconds, it is dropped 263 | conf_dict.setdefault("CLIENT_ACK_TIMEOUT", 5*60) 264 | 265 | # telegram servers connect timeout in seconds 266 | conf_dict.setdefault("TG_CONNECT_TIMEOUT", 10) 267 | 268 | # listen address for IPv4 269 | conf_dict.setdefault("LISTEN_ADDR_IPV4", "0.0.0.0") 270 | 271 | # listen address for IPv6 272 | conf_dict.setdefault("LISTEN_ADDR_IPV6", "::") 273 | 274 | # listen unix socket 275 | conf_dict.setdefault("LISTEN_UNIX_SOCK", "") 276 | 277 | # prometheus exporter listen port, use some random port here 278 | conf_dict.setdefault("METRICS_PORT", None) 279 | 280 | # prometheus listen addr ipv4 281 | conf_dict.setdefault("METRICS_LISTEN_ADDR_IPV4", "0.0.0.0") 282 | 283 | # prometheus listen addr ipv6 284 | conf_dict.setdefault("METRICS_LISTEN_ADDR_IPV6", None) 285 | 286 | # prometheus scrapers whitelist 287 | conf_dict.setdefault("METRICS_WHITELIST", ["127.0.0.1", "::1"]) 288 | 289 | # export proxy link to prometheus 290 | conf_dict.setdefault("METRICS_EXPORT_LINKS", False) 291 | 292 | # default prefix for metrics 293 | conf_dict.setdefault("METRICS_PREFIX", "mtprotoproxy_") 294 | 295 | # allow access to config by attributes 296 | config = type("config", (dict,), conf_dict)(conf_dict) 297 | 298 | 299 | def apply_upstream_proxy_settings(): 300 | # apply socks settings in place 301 | if config.SOCKS5_HOST and config.SOCKS5_PORT: 302 | import socks 303 | print_err("套接字代理模式已激活,与广告和 uvloop 不兼容") 304 | socks.set_default_proxy(socks.PROXY_TYPE_SOCKS5, config.SOCKS5_HOST, config.SOCKS5_PORT, 305 | username=config.SOCKS5_USER, password=config.SOCKS5_PASS) 306 | if not hasattr(socket, "origsocket"): 307 | socket.origsocket = socket.socket 308 | socket.socket = socks.socksocket 309 | elif hasattr(socket, "origsocket"): 310 | socket.socket = socket.origsocket 311 | del socket.origsocket 312 | 313 | 314 | def try_use_cryptography_module(): 315 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 316 | from cryptography.hazmat.backends import default_backend 317 | 318 | class CryptographyEncryptorAdapter: 319 | __slots__ = ('encryptor', 'decryptor') 320 | 321 | def __init__(self, cipher): 322 | self.encryptor = cipher.encryptor() 323 | self.decryptor = cipher.decryptor() 324 | 325 | def encrypt(self, data): 326 | return self.encryptor.update(data) 327 | 328 | def decrypt(self, data): 329 | return self.decryptor.update(data) 330 | 331 | def create_aes_ctr(key, iv): 332 | iv_bytes = int.to_bytes(iv, 16, "big") 333 | cipher = Cipher(algorithms.AES(key), modes.CTR(iv_bytes), default_backend()) 334 | return CryptographyEncryptorAdapter(cipher) 335 | 336 | def create_aes_cbc(key, iv): 337 | cipher = Cipher(algorithms.AES(key), modes.CBC(iv), default_backend()) 338 | return CryptographyEncryptorAdapter(cipher) 339 | 340 | return create_aes_ctr, create_aes_cbc 341 | 342 | 343 | def try_use_pycrypto_or_pycryptodome_module(): 344 | from Crypto.Cipher import AES 345 | from Crypto.Util import Counter 346 | 347 | def create_aes_ctr(key, iv): 348 | ctr = Counter.new(128, initial_value=iv) 349 | return AES.new(key, AES.MODE_CTR, counter=ctr) 350 | 351 | def create_aes_cbc(key, iv): 352 | return AES.new(key, AES.MODE_CBC, iv) 353 | 354 | return create_aes_ctr, create_aes_cbc 355 | 356 | 357 | def use_slow_bundled_cryptography_module(): 358 | import pyaes 359 | 360 | msg = "为了使程序运行速度更快,请安装加密模块:" 361 | msg += "pip install cryptography\n" 362 | print(msg, flush=True, file=sys.stderr) 363 | 364 | class BundledEncryptorAdapter: 365 | __slots__ = ('mode', ) 366 | 367 | def __init__(self, mode): 368 | self.mode = mode 369 | 370 | def encrypt(self, data): 371 | encrypter = pyaes.Encrypter(self.mode, pyaes.PADDING_NONE) 372 | return encrypter.feed(data) + encrypter.feed() 373 | 374 | def decrypt(self, data): 375 | decrypter = pyaes.Decrypter(self.mode, pyaes.PADDING_NONE) 376 | return decrypter.feed(data) + decrypter.feed() 377 | 378 | def create_aes_ctr(key, iv): 379 | ctr = pyaes.Counter(iv) 380 | return pyaes.AESModeOfOperationCTR(key, ctr) 381 | 382 | def create_aes_cbc(key, iv): 383 | mode = pyaes.AESModeOfOperationCBC(key, iv) 384 | return BundledEncryptorAdapter(mode) 385 | return create_aes_ctr, create_aes_cbc 386 | 387 | 388 | try: 389 | create_aes_ctr, create_aes_cbc = try_use_cryptography_module() 390 | except ImportError: 391 | try: 392 | create_aes_ctr, create_aes_cbc = try_use_pycrypto_or_pycryptodome_module() 393 | except ImportError: 394 | create_aes_ctr, create_aes_cbc = use_slow_bundled_cryptography_module() 395 | 396 | 397 | def print_err(*params): 398 | print(*params, file=sys.stderr, flush=True) 399 | 400 | 401 | def ensure_users_in_user_stats(): 402 | global user_stats 403 | 404 | for user in config.USERS: 405 | user_stats[user].update() 406 | 407 | 408 | def init_proxy_start_time(): 409 | global proxy_start_time 410 | proxy_start_time = time.time() 411 | 412 | 413 | def update_stats(**kw_stats): 414 | global stats 415 | stats.update(**kw_stats) 416 | 417 | 418 | def update_user_stats(user, **kw_stats): 419 | global user_stats 420 | user_stats[user].update(**kw_stats) 421 | 422 | 423 | def update_durations(duration): 424 | global stats 425 | 426 | for bucket in STAT_DURATION_BUCKETS: 427 | if duration <= bucket: 428 | break 429 | 430 | update_stats(**{"connects_with_duration_le_%s" % str(bucket): 1}) 431 | 432 | 433 | def get_curr_connects_count(): 434 | global user_stats 435 | 436 | all_connects = 0 437 | for user, stat in user_stats.items(): 438 | all_connects += stat["curr_connects"] 439 | return all_connects 440 | 441 | 442 | def get_to_tg_bufsize(): 443 | if isinstance(config.TO_TG_BUFSIZE, int): 444 | return config.TO_TG_BUFSIZE 445 | 446 | low, margin, high = config.TO_TG_BUFSIZE 447 | return high if get_curr_connects_count() < margin else low 448 | 449 | 450 | def get_to_clt_bufsize(): 451 | if isinstance(config.TO_CLT_BUFSIZE, int): 452 | return config.TO_CLT_BUFSIZE 453 | 454 | low, margin, high = config.TO_CLT_BUFSIZE 455 | return high if get_curr_connects_count() < margin else low 456 | 457 | 458 | class MyRandom(random.Random): 459 | def __init__(self): 460 | super().__init__() 461 | key = bytes([random.randrange(256) for i in range(32)]) 462 | iv = random.randrange(256**16) 463 | 464 | self.encryptor = create_aes_ctr(key, iv) 465 | self.buffer = bytearray() 466 | 467 | def getrandbits(self, k): 468 | numbytes = (k + 7) // 8 469 | return int.from_bytes(self.getrandbytes(numbytes), 'big') >> (numbytes * 8 - k) 470 | 471 | def getrandbytes(self, n): 472 | CHUNK_SIZE = 512 473 | 474 | while n > len(self.buffer): 475 | data = int.to_bytes(super().getrandbits(CHUNK_SIZE*8), CHUNK_SIZE, "big") 476 | self.buffer += self.encryptor.encrypt(data) 477 | 478 | result = self.buffer[:n] 479 | self.buffer = self.buffer[n:] 480 | return bytes(result) 481 | 482 | 483 | myrandom = MyRandom() 484 | 485 | 486 | class TgConnectionPool: 487 | MAX_CONNS_IN_POOL = 0 488 | 489 | def __init__(self): 490 | self.pools = {} 491 | 492 | async def open_tg_connection(self, host, port, init_func=None): 493 | task = asyncio.open_connection(host, port, limit=get_to_clt_bufsize()) 494 | reader_tgt, writer_tgt = await asyncio.wait_for(task, timeout=config.TG_CONNECT_TIMEOUT) 495 | 496 | set_keepalive(writer_tgt.get_extra_info("socket")) 497 | set_bufsizes(writer_tgt.get_extra_info("socket"), get_to_clt_bufsize(), get_to_tg_bufsize()) 498 | 499 | if init_func: 500 | return await asyncio.wait_for(init_func(host, port, reader_tgt, writer_tgt), 501 | timeout=config.TG_CONNECT_TIMEOUT) 502 | return reader_tgt, writer_tgt 503 | 504 | def register_host_port(self, host, port, init_func): 505 | if (host, port, init_func) not in self.pools: 506 | self.pools[(host, port, init_func)] = [] 507 | 508 | while len(self.pools[(host, port, init_func)]) < TgConnectionPool.MAX_CONNS_IN_POOL: 509 | connect_task = asyncio.ensure_future(self.open_tg_connection(host, port, init_func)) 510 | self.pools[(host, port, init_func)].append(connect_task) 511 | 512 | async def get_connection(self, host, port, init_func=None): 513 | self.register_host_port(host, port, init_func) 514 | 515 | ret = None 516 | for task in self.pools[(host, port, init_func)][::]: 517 | if task.done(): 518 | if task.exception(): 519 | self.pools[(host, port, init_func)].remove(task) 520 | continue 521 | 522 | reader, writer, *other = task.result() 523 | if writer.transport.is_closing(): 524 | self.pools[(host, port, init_func)].remove(task) 525 | continue 526 | 527 | if not ret: 528 | self.pools[(host, port, init_func)].remove(task) 529 | ret = (reader, writer, *other) 530 | 531 | self.register_host_port(host, port, init_func) 532 | if ret: 533 | return ret 534 | return await self.open_tg_connection(host, port, init_func) 535 | 536 | 537 | tg_connection_pool = TgConnectionPool() 538 | 539 | 540 | class LayeredStreamReaderBase: 541 | __slots__ = ("upstream", ) 542 | 543 | def __init__(self, upstream): 544 | self.upstream = upstream 545 | 546 | async def read(self, n): 547 | return await self.upstream.read(n) 548 | 549 | async def readexactly(self, n): 550 | return await self.upstream.readexactly(n) 551 | 552 | 553 | class LayeredStreamWriterBase: 554 | __slots__ = ("upstream", ) 555 | 556 | def __init__(self, upstream): 557 | self.upstream = upstream 558 | 559 | def write(self, data, extra={}): 560 | return self.upstream.write(data) 561 | 562 | def write_eof(self): 563 | return self.upstream.write_eof() 564 | 565 | async def drain(self): 566 | return await self.upstream.drain() 567 | 568 | def close(self): 569 | return self.upstream.close() 570 | 571 | def abort(self): 572 | return self.upstream.transport.abort() 573 | 574 | def get_extra_info(self, name): 575 | return self.upstream.get_extra_info(name) 576 | 577 | @property 578 | def transport(self): 579 | return self.upstream.transport 580 | 581 | 582 | class FakeTLSStreamReader(LayeredStreamReaderBase): 583 | __slots__ = ('buf', ) 584 | 585 | def __init__(self, upstream): 586 | self.upstream = upstream 587 | self.buf = bytearray() 588 | 589 | async def read(self, n, ignore_buf=False): 590 | if self.buf and not ignore_buf: 591 | data = self.buf 592 | self.buf = bytearray() 593 | return bytes(data) 594 | 595 | while True: 596 | tls_rec_type = await self.upstream.readexactly(1) 597 | if not tls_rec_type: 598 | return b"" 599 | 600 | if tls_rec_type not in [b"\x14", b"\x17"]: 601 | print_err("BUG: bad tls type %s in FakeTLSStreamReader" % tls_rec_type) 602 | return b"" 603 | 604 | version = await self.upstream.readexactly(2) 605 | if version != b"\x03\x03": 606 | print_err("BUG: unknown version %s in FakeTLSStreamReader" % version) 607 | return b"" 608 | 609 | data_len = int.from_bytes(await self.upstream.readexactly(2), "big") 610 | data = await self.upstream.readexactly(data_len) 611 | if tls_rec_type == b"\x14": 612 | continue 613 | return data 614 | 615 | async def readexactly(self, n): 616 | while len(self.buf) < n: 617 | tls_data = await self.read(1, ignore_buf=True) 618 | if not tls_data: 619 | return b"" 620 | self.buf += tls_data 621 | data, self.buf = self.buf[:n], self.buf[n:] 622 | return bytes(data) 623 | 624 | 625 | class FakeTLSStreamWriter(LayeredStreamWriterBase): 626 | __slots__ = () 627 | 628 | def __init__(self, upstream): 629 | self.upstream = upstream 630 | 631 | def write(self, data, extra={}): 632 | MAX_CHUNK_SIZE = 16384 + 24 633 | for start in range(0, len(data), MAX_CHUNK_SIZE): 634 | end = min(start+MAX_CHUNK_SIZE, len(data)) 635 | self.upstream.write(b"\x17\x03\x03" + int.to_bytes(end-start, 2, "big")) 636 | self.upstream.write(data[start: end]) 637 | return len(data) 638 | 639 | 640 | class CryptoWrappedStreamReader(LayeredStreamReaderBase): 641 | __slots__ = ('decryptor', 'block_size', 'buf') 642 | 643 | def __init__(self, upstream, decryptor, block_size=1): 644 | self.upstream = upstream 645 | self.decryptor = decryptor 646 | self.block_size = block_size 647 | self.buf = bytearray() 648 | 649 | async def read(self, n): 650 | if self.buf: 651 | ret = bytes(self.buf) 652 | self.buf.clear() 653 | return ret 654 | else: 655 | data = await self.upstream.read(n) 656 | if not data: 657 | return b"" 658 | 659 | needed_till_full_block = -len(data) % self.block_size 660 | if needed_till_full_block > 0: 661 | data += self.upstream.readexactly(needed_till_full_block) 662 | return self.decryptor.decrypt(data) 663 | 664 | async def readexactly(self, n): 665 | if n > len(self.buf): 666 | to_read = n - len(self.buf) 667 | needed_till_full_block = -to_read % self.block_size 668 | 669 | to_read_block_aligned = to_read + needed_till_full_block 670 | data = await self.upstream.readexactly(to_read_block_aligned) 671 | self.buf += self.decryptor.decrypt(data) 672 | 673 | ret = bytes(self.buf[:n]) 674 | self.buf = self.buf[n:] 675 | return ret 676 | 677 | 678 | class CryptoWrappedStreamWriter(LayeredStreamWriterBase): 679 | __slots__ = ('encryptor', 'block_size') 680 | 681 | def __init__(self, upstream, encryptor, block_size=1): 682 | self.upstream = upstream 683 | self.encryptor = encryptor 684 | self.block_size = block_size 685 | 686 | def write(self, data, extra={}): 687 | if len(data) % self.block_size != 0: 688 | print_err("BUG: writing %d bytes not aligned to block size %d" % ( 689 | len(data), self.block_size)) 690 | return 0 691 | q = self.encryptor.encrypt(data) 692 | return self.upstream.write(q) 693 | 694 | 695 | class MTProtoFrameStreamReader(LayeredStreamReaderBase): 696 | __slots__ = ('seq_no', ) 697 | 698 | def __init__(self, upstream, seq_no=0): 699 | self.upstream = upstream 700 | self.seq_no = seq_no 701 | 702 | async def read(self, buf_size): 703 | msg_len_bytes = await self.upstream.readexactly(4) 704 | msg_len = int.from_bytes(msg_len_bytes, "little") 705 | # skip paddings 706 | while msg_len == 4: 707 | msg_len_bytes = await self.upstream.readexactly(4) 708 | msg_len = int.from_bytes(msg_len_bytes, "little") 709 | 710 | len_is_bad = (msg_len % len(PADDING_FILLER) != 0) 711 | if not MIN_MSG_LEN <= msg_len <= MAX_MSG_LEN or len_is_bad: 712 | print_err("msg_len is bad, closing connection", msg_len) 713 | return b"" 714 | 715 | msg_seq_bytes = await self.upstream.readexactly(4) 716 | msg_seq = int.from_bytes(msg_seq_bytes, "little", signed=True) 717 | if msg_seq != self.seq_no: 718 | print_err("unexpected seq_no") 719 | return b"" 720 | 721 | self.seq_no += 1 722 | 723 | data = await self.upstream.readexactly(msg_len - 4 - 4 - 4) 724 | checksum_bytes = await self.upstream.readexactly(4) 725 | checksum = int.from_bytes(checksum_bytes, "little") 726 | 727 | computed_checksum = binascii.crc32(msg_len_bytes + msg_seq_bytes + data) 728 | if computed_checksum != checksum: 729 | return b"" 730 | return data 731 | 732 | 733 | class MTProtoFrameStreamWriter(LayeredStreamWriterBase): 734 | __slots__ = ('seq_no', ) 735 | 736 | def __init__(self, upstream, seq_no=0): 737 | self.upstream = upstream 738 | self.seq_no = seq_no 739 | 740 | def write(self, msg, extra={}): 741 | len_bytes = int.to_bytes(len(msg) + 4 + 4 + 4, 4, "little") 742 | seq_bytes = int.to_bytes(self.seq_no, 4, "little", signed=True) 743 | self.seq_no += 1 744 | 745 | msg_without_checksum = len_bytes + seq_bytes + msg 746 | checksum = int.to_bytes(binascii.crc32(msg_without_checksum), 4, "little") 747 | 748 | full_msg = msg_without_checksum + checksum 749 | padding = PADDING_FILLER * ((-len(full_msg) % CBC_PADDING) // len(PADDING_FILLER)) 750 | 751 | return self.upstream.write(full_msg + padding) 752 | 753 | 754 | class MTProtoCompactFrameStreamReader(LayeredStreamReaderBase): 755 | __slots__ = () 756 | 757 | async def read(self, buf_size): 758 | msg_len_bytes = await self.upstream.readexactly(1) 759 | msg_len = int.from_bytes(msg_len_bytes, "little") 760 | 761 | extra = {"QUICKACK_FLAG": False} 762 | if msg_len >= 0x80: 763 | extra["QUICKACK_FLAG"] = True 764 | msg_len -= 0x80 765 | 766 | if msg_len == 0x7f: 767 | msg_len_bytes = await self.upstream.readexactly(3) 768 | msg_len = int.from_bytes(msg_len_bytes, "little") 769 | 770 | msg_len *= 4 771 | 772 | data = await self.upstream.readexactly(msg_len) 773 | 774 | return data, extra 775 | 776 | 777 | class MTProtoCompactFrameStreamWriter(LayeredStreamWriterBase): 778 | __slots__ = () 779 | 780 | def write(self, data, extra={}): 781 | SMALL_PKT_BORDER = 0x7f 782 | LARGE_PKT_BORGER = 256 ** 3 783 | 784 | if len(data) % 4 != 0: 785 | print_err("BUG: MTProtoFrameStreamWriter attempted to send msg with len", len(data)) 786 | return 0 787 | 788 | if extra.get("SIMPLE_ACK"): 789 | return self.upstream.write(data[::-1]) 790 | 791 | len_div_four = len(data) // 4 792 | 793 | if len_div_four < SMALL_PKT_BORDER: 794 | return self.upstream.write(bytes([len_div_four]) + data) 795 | elif len_div_four < LARGE_PKT_BORGER: 796 | return self.upstream.write(b'\x7f' + int.to_bytes(len_div_four, 3, 'little') + data) 797 | else: 798 | print_err("尝试发送的数据包长度过大 =", len(data)) 799 | return 0 800 | 801 | 802 | class MTProtoIntermediateFrameStreamReader(LayeredStreamReaderBase): 803 | __slots__ = () 804 | 805 | async def read(self, buf_size): 806 | msg_len_bytes = await self.upstream.readexactly(4) 807 | msg_len = int.from_bytes(msg_len_bytes, "little") 808 | 809 | extra = {} 810 | if msg_len > 0x80000000: 811 | extra["QUICKACK_FLAG"] = True 812 | msg_len -= 0x80000000 813 | 814 | data = await self.upstream.readexactly(msg_len) 815 | return data, extra 816 | 817 | 818 | class MTProtoIntermediateFrameStreamWriter(LayeredStreamWriterBase): 819 | __slots__ = () 820 | 821 | def write(self, data, extra={}): 822 | if extra.get("SIMPLE_ACK"): 823 | return self.upstream.write(data) 824 | else: 825 | return self.upstream.write(int.to_bytes(len(data), 4, 'little') + data) 826 | 827 | 828 | class MTProtoSecureIntermediateFrameStreamReader(LayeredStreamReaderBase): 829 | __slots__ = () 830 | 831 | async def read(self, buf_size): 832 | msg_len_bytes = await self.upstream.readexactly(4) 833 | msg_len = int.from_bytes(msg_len_bytes, "little") 834 | 835 | extra = {} 836 | if msg_len > 0x80000000: 837 | extra["QUICKACK_FLAG"] = True 838 | msg_len -= 0x80000000 839 | 840 | data = await self.upstream.readexactly(msg_len) 841 | 842 | if msg_len % 4 != 0: 843 | cut_border = msg_len - (msg_len % 4) 844 | data = data[:cut_border] 845 | 846 | return data, extra 847 | 848 | 849 | class MTProtoSecureIntermediateFrameStreamWriter(LayeredStreamWriterBase): 850 | __slots__ = () 851 | 852 | def write(self, data, extra={}): 853 | MAX_PADDING_LEN = 4 854 | if extra.get("SIMPLE_ACK"): 855 | # TODO: make this unpredictable 856 | return self.upstream.write(data) 857 | else: 858 | padding_len = myrandom.randrange(MAX_PADDING_LEN) 859 | padding = myrandom.getrandbytes(padding_len) 860 | padded_data_len_bytes = int.to_bytes(len(data) + padding_len, 4, 'little') 861 | return self.upstream.write(padded_data_len_bytes + data + padding) 862 | 863 | 864 | class ProxyReqStreamReader(LayeredStreamReaderBase): 865 | __slots__ = () 866 | 867 | async def read(self, msg): 868 | RPC_PROXY_ANS = b"\x0d\xda\x03\x44" 869 | RPC_CLOSE_EXT = b"\xa2\x34\xb6\x5e" 870 | RPC_SIMPLE_ACK = b"\x9b\x40\xac\x3b" 871 | RPC_UNKNOWN = b'\xdf\xa2\x30\x57' 872 | 873 | data = await self.upstream.read(1) 874 | 875 | if len(data) < 4: 876 | return b"" 877 | 878 | ans_type = data[:4] 879 | if ans_type == RPC_CLOSE_EXT: 880 | return b"" 881 | 882 | if ans_type == RPC_PROXY_ANS: 883 | ans_flags, conn_id, conn_data = data[4:8], data[8:16], data[16:] 884 | return conn_data 885 | 886 | if ans_type == RPC_SIMPLE_ACK: 887 | conn_id, confirm = data[4:12], data[12:16] 888 | return confirm, {"SIMPLE_ACK": True} 889 | 890 | if ans_type == RPC_UNKNOWN: 891 | return b"", {"SKIP_SEND": True} 892 | 893 | print_err("unknown rpc ans type:", ans_type) 894 | return b"", {"SKIP_SEND": True} 895 | 896 | 897 | class ProxyReqStreamWriter(LayeredStreamWriterBase): 898 | __slots__ = ('remote_ip_port', 'our_ip_port', 'out_conn_id', 'proto_tag') 899 | 900 | def __init__(self, upstream, cl_ip, cl_port, my_ip, my_port, proto_tag): 901 | self.upstream = upstream 902 | 903 | if ":" not in cl_ip: 904 | self.remote_ip_port = b"\x00" * 10 + b"\xff\xff" 905 | self.remote_ip_port += socket.inet_pton(socket.AF_INET, cl_ip) 906 | else: 907 | self.remote_ip_port = socket.inet_pton(socket.AF_INET6, cl_ip) 908 | self.remote_ip_port += int.to_bytes(cl_port, 4, "little") 909 | 910 | if ":" not in my_ip: 911 | self.our_ip_port = b"\x00" * 10 + b"\xff\xff" 912 | self.our_ip_port += socket.inet_pton(socket.AF_INET, my_ip) 913 | else: 914 | self.our_ip_port = socket.inet_pton(socket.AF_INET6, my_ip) 915 | self.our_ip_port += int.to_bytes(my_port, 4, "little") 916 | self.out_conn_id = myrandom.getrandbytes(8) 917 | 918 | self.proto_tag = proto_tag 919 | 920 | def write(self, msg, extra={}): 921 | RPC_PROXY_REQ = b"\xee\xf1\xce\x36" 922 | EXTRA_SIZE = b"\x18\x00\x00\x00" 923 | PROXY_TAG = b"\xae\x26\x1e\xdb" 924 | FOUR_BYTES_ALIGNER = b"\x00\x00\x00" 925 | 926 | FLAG_NOT_ENCRYPTED = 0x2 927 | FLAG_HAS_AD_TAG = 0x8 928 | FLAG_MAGIC = 0x1000 929 | FLAG_EXTMODE2 = 0x20000 930 | FLAG_PAD = 0x8000000 931 | FLAG_INTERMEDIATE = 0x20000000 932 | FLAG_ABRIDGED = 0x40000000 933 | FLAG_QUICKACK = 0x80000000 934 | 935 | if len(msg) % 4 != 0: 936 | print_err("BUG: 尝试发送长度为 len 的消息 %d" % len(msg)) 937 | return 0 938 | 939 | flags = FLAG_HAS_AD_TAG | FLAG_MAGIC | FLAG_EXTMODE2 940 | 941 | if self.proto_tag == PROTO_TAG_ABRIDGED: 942 | flags |= FLAG_ABRIDGED 943 | elif self.proto_tag == PROTO_TAG_INTERMEDIATE: 944 | flags |= FLAG_INTERMEDIATE 945 | elif self.proto_tag == PROTO_TAG_SECURE: 946 | flags |= FLAG_INTERMEDIATE | FLAG_PAD 947 | 948 | if extra.get("QUICKACK_FLAG"): 949 | flags |= FLAG_QUICKACK 950 | 951 | if msg.startswith(b"\x00" * 8): 952 | flags |= FLAG_NOT_ENCRYPTED 953 | 954 | full_msg = bytearray() 955 | full_msg += RPC_PROXY_REQ + int.to_bytes(flags, 4, "little") + self.out_conn_id 956 | full_msg += self.remote_ip_port + self.our_ip_port + EXTRA_SIZE + PROXY_TAG 957 | full_msg += bytes([len(config.AD_TAG)]) + config.AD_TAG + FOUR_BYTES_ALIGNER 958 | full_msg += msg 959 | 960 | return self.upstream.write(full_msg) 961 | 962 | 963 | def try_setsockopt(sock, level, option, value): 964 | try: 965 | sock.setsockopt(level, option, value) 966 | except OSError as E: 967 | pass 968 | 969 | 970 | def set_keepalive(sock, interval=40, attempts=5): 971 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 972 | if hasattr(socket, "TCP_KEEPIDLE"): 973 | try_setsockopt(sock, socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, interval) 974 | if hasattr(socket, "TCP_KEEPINTVL"): 975 | try_setsockopt(sock, socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval) 976 | if hasattr(socket, "TCP_KEEPCNT"): 977 | try_setsockopt(sock, socket.IPPROTO_TCP, socket.TCP_KEEPCNT, attempts) 978 | 979 | 980 | def set_ack_timeout(sock, timeout): 981 | if hasattr(socket, "TCP_USER_TIMEOUT"): 982 | try_setsockopt(sock, socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, timeout*1000) 983 | 984 | 985 | def set_bufsizes(sock, recv_buf, send_buf): 986 | try_setsockopt(sock, socket.SOL_SOCKET, socket.SO_RCVBUF, recv_buf) 987 | try_setsockopt(sock, socket.SOL_SOCKET, socket.SO_SNDBUF, send_buf) 988 | 989 | 990 | def set_instant_rst(sock): 991 | INSTANT_RST = b"\x01\x00\x00\x00\x00\x00\x00\x00" 992 | if hasattr(socket, "SO_LINGER"): 993 | try_setsockopt(sock, socket.SOL_SOCKET, socket.SO_LINGER, INSTANT_RST) 994 | 995 | 996 | def gen_x25519_public_key(): 997 | # generates some number which has square root by modulo P 998 | P = 2**255 - 19 999 | n = myrandom.randrange(P) 1000 | return int.to_bytes((n*n) % P, length=32, byteorder="little") 1001 | 1002 | 1003 | async def connect_reader_to_writer(reader, writer): 1004 | BUF_SIZE = 8192 1005 | try: 1006 | while True: 1007 | data = await reader.read(BUF_SIZE) 1008 | 1009 | if not data: 1010 | if not writer.transport.is_closing(): 1011 | writer.write_eof() 1012 | await writer.drain() 1013 | return 1014 | 1015 | writer.write(data) 1016 | await writer.drain() 1017 | except (OSError, asyncio.IncompleteReadError) as e: 1018 | pass 1019 | 1020 | 1021 | async def handle_bad_client(reader_clt, writer_clt, handshake): 1022 | BUF_SIZE = 8192 1023 | CONNECT_TIMEOUT = 5 1024 | 1025 | global mask_host_cached_ip 1026 | 1027 | update_stats(connects_bad=1) 1028 | 1029 | if writer_clt.transport.is_closing(): 1030 | return 1031 | 1032 | set_bufsizes(writer_clt.get_extra_info("socket"), BUF_SIZE, BUF_SIZE) 1033 | 1034 | if not config.MASK or handshake is None: 1035 | while await reader_clt.read(BUF_SIZE): 1036 | # just consume all the data 1037 | pass 1038 | return 1039 | 1040 | writer_srv = None 1041 | try: 1042 | host = mask_host_cached_ip or config.MASK_HOST 1043 | task = asyncio.open_connection(host, config.MASK_PORT, limit=BUF_SIZE) 1044 | reader_srv, writer_srv = await asyncio.wait_for(task, timeout=CONNECT_TIMEOUT) 1045 | if not mask_host_cached_ip: 1046 | mask_host_cached_ip = writer_srv.get_extra_info("peername")[0] 1047 | writer_srv.write(handshake) 1048 | await writer_srv.drain() 1049 | 1050 | srv_to_clt = connect_reader_to_writer(reader_srv, writer_clt) 1051 | clt_to_srv = connect_reader_to_writer(reader_clt, writer_srv) 1052 | task_srv_to_clt = asyncio.ensure_future(srv_to_clt) 1053 | task_clt_to_srv = asyncio.ensure_future(clt_to_srv) 1054 | 1055 | await asyncio.wait([task_srv_to_clt, task_clt_to_srv], return_when=asyncio.FIRST_COMPLETED) 1056 | 1057 | task_srv_to_clt.cancel() 1058 | task_clt_to_srv.cancel() 1059 | 1060 | if writer_clt.transport.is_closing(): 1061 | return 1062 | 1063 | # if the server closed the connection with RST or FIN-RST, copy them to the client 1064 | if not writer_srv.transport.is_closing(): 1065 | # workaround for uvloop, it doesn't fire exceptions on write_eof 1066 | sock = writer_srv.get_extra_info('socket') 1067 | raw_sock = socket.socket(sock.family, sock.type, sock.proto, sock.fileno()) 1068 | try: 1069 | raw_sock.shutdown(socket.SHUT_WR) 1070 | except OSError as E: 1071 | set_instant_rst(writer_clt.get_extra_info("socket")) 1072 | finally: 1073 | raw_sock.detach() 1074 | else: 1075 | set_instant_rst(writer_clt.get_extra_info("socket")) 1076 | except ConnectionRefusedError as E: 1077 | return 1078 | except (OSError, asyncio.TimeoutError) as E: 1079 | return 1080 | finally: 1081 | if writer_srv is not None: 1082 | writer_srv.transport.abort() 1083 | 1084 | 1085 | async def handle_fake_tls_handshake(handshake, reader, writer, peer): 1086 | global used_handshakes 1087 | global client_ips 1088 | global last_client_ips 1089 | global last_clients_with_time_skew 1090 | global last_clients_with_same_handshake 1091 | global fake_cert_len 1092 | 1093 | TIME_SKEW_MIN = -20 * 60 1094 | TIME_SKEW_MAX = 10 * 60 1095 | 1096 | TLS_VERS = b"\x03\x03" 1097 | TLS_CIPHERSUITE = b"\x13\x01" 1098 | TLS_CHANGE_CIPHER = b"\x14" + TLS_VERS + b"\x00\x01\x01" 1099 | TLS_APP_HTTP2_HDR = b"\x17" + TLS_VERS 1100 | 1101 | DIGEST_LEN = 32 1102 | DIGEST_HALFLEN = 16 1103 | DIGEST_POS = 11 1104 | 1105 | SESSION_ID_LEN_POS = DIGEST_POS + DIGEST_LEN 1106 | SESSION_ID_POS = SESSION_ID_LEN_POS + 1 1107 | 1108 | tls_extensions = b"\x00\x2e" + b"\x00\x33\x00\x24" + b"\x00\x1d\x00\x20" 1109 | tls_extensions += gen_x25519_public_key() + b"\x00\x2b\x00\x02\x03\x04" 1110 | 1111 | digest = handshake[DIGEST_POS:DIGEST_POS+DIGEST_LEN] 1112 | 1113 | if digest[:DIGEST_HALFLEN] in used_handshakes: 1114 | last_clients_with_same_handshake[peer[0]] += 1 1115 | return False 1116 | 1117 | sess_id_len = handshake[SESSION_ID_LEN_POS] 1118 | sess_id = handshake[SESSION_ID_POS:SESSION_ID_POS+sess_id_len] 1119 | 1120 | for user in config.USERS: 1121 | secret = bytes.fromhex(config.USERS[user]) 1122 | 1123 | msg = handshake[:DIGEST_POS] + b"\x00"*DIGEST_LEN + handshake[DIGEST_POS+DIGEST_LEN:] 1124 | computed_digest = hmac.new(secret, msg, digestmod=hashlib.sha256).digest() 1125 | 1126 | xored_digest = bytes(digest[i] ^ computed_digest[i] for i in range(DIGEST_LEN)) 1127 | digest_good = xored_digest.startswith(b"\x00" * (DIGEST_LEN-4)) 1128 | 1129 | if not digest_good: 1130 | continue 1131 | 1132 | timestamp = int.from_bytes(xored_digest[-4:], "little") 1133 | client_time_is_ok = TIME_SKEW_MIN < time.time() - timestamp < TIME_SKEW_MAX 1134 | 1135 | # some clients fail to read unix time and send the time since boot instead 1136 | client_time_is_small = timestamp < 60*60*24*1000 1137 | accept_bad_time = config.IGNORE_TIME_SKEW or is_time_skewed or client_time_is_small 1138 | 1139 | if not client_time_is_ok and not accept_bad_time: 1140 | last_clients_with_time_skew[peer[0]] = (time.time() - timestamp) // 60 1141 | continue 1142 | 1143 | http_data = myrandom.getrandbytes(fake_cert_len) 1144 | 1145 | srv_hello = TLS_VERS + b"\x00"*DIGEST_LEN + bytes([sess_id_len]) + sess_id 1146 | srv_hello += TLS_CIPHERSUITE + b"\x00" + tls_extensions 1147 | 1148 | hello_pkt = b"\x16" + TLS_VERS + int.to_bytes(len(srv_hello) + 4, 2, "big") 1149 | hello_pkt += b"\x02" + int.to_bytes(len(srv_hello), 3, "big") + srv_hello 1150 | hello_pkt += TLS_CHANGE_CIPHER + TLS_APP_HTTP2_HDR 1151 | hello_pkt += int.to_bytes(len(http_data), 2, "big") + http_data 1152 | 1153 | computed_digest = hmac.new(secret, msg=digest+hello_pkt, digestmod=hashlib.sha256).digest() 1154 | hello_pkt = hello_pkt[:DIGEST_POS] + computed_digest + hello_pkt[DIGEST_POS+DIGEST_LEN:] 1155 | 1156 | writer.write(hello_pkt) 1157 | await writer.drain() 1158 | 1159 | if config.REPLAY_CHECK_LEN > 0: 1160 | while len(used_handshakes) >= config.REPLAY_CHECK_LEN: 1161 | used_handshakes.popitem(last=False) 1162 | used_handshakes[digest[:DIGEST_HALFLEN]] = True 1163 | 1164 | if config.CLIENT_IPS_LEN > 0: 1165 | while len(client_ips) >= config.CLIENT_IPS_LEN: 1166 | client_ips.popitem(last=False) 1167 | if peer[0] not in client_ips: 1168 | client_ips[peer[0]] = True 1169 | last_client_ips[peer[0]] = True 1170 | 1171 | reader = FakeTLSStreamReader(reader) 1172 | writer = FakeTLSStreamWriter(writer) 1173 | return reader, writer 1174 | 1175 | return False 1176 | 1177 | 1178 | async def handle_proxy_protocol(reader, peer=None): 1179 | PROXY_SIGNATURE = b"PROXY " 1180 | PROXY_MIN_LEN = 6 1181 | PROXY_TCP4 = b"TCP4" 1182 | PROXY_TCP6 = b"TCP6" 1183 | PROXY_UNKNOWN = b"UNKNOWN" 1184 | 1185 | PROXY2_SIGNATURE = b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a" 1186 | PROXY2_MIN_LEN = 16 1187 | PROXY2_AF_UNSPEC = 0x0 1188 | PROXY2_AF_INET = 0x1 1189 | PROXY2_AF_INET6 = 0x2 1190 | 1191 | header = await reader.readexactly(PROXY_MIN_LEN) 1192 | if header.startswith(PROXY_SIGNATURE): 1193 | # proxy header v1 1194 | header += await reader.readuntil(b"\r\n") 1195 | _, proxy_fam, *proxy_addr = header[:-2].split(b" ") 1196 | if proxy_fam in (PROXY_TCP4, PROXY_TCP6): 1197 | if len(proxy_addr) == 4: 1198 | src_addr = proxy_addr[0].decode('ascii') 1199 | src_port = int(proxy_addr[2].decode('ascii')) 1200 | return (src_addr, src_port) 1201 | elif proxy_fam == PROXY_UNKNOWN: 1202 | return peer 1203 | return False 1204 | 1205 | header += await reader.readexactly(PROXY2_MIN_LEN - PROXY_MIN_LEN) 1206 | if header.startswith(PROXY2_SIGNATURE): 1207 | # proxy header v2 1208 | proxy_ver = header[12] 1209 | if proxy_ver & 0xf0 != 0x20: 1210 | return False 1211 | proxy_len = int.from_bytes(header[14:16], "big") 1212 | proxy_addr = await reader.readexactly(proxy_len) 1213 | if proxy_ver == 0x21: 1214 | proxy_fam = header[13] >> 4 1215 | if proxy_fam == PROXY2_AF_INET: 1216 | if proxy_len >= (4 + 2)*2: 1217 | src_addr = socket.inet_ntop(socket.AF_INET, proxy_addr[:4]) 1218 | src_port = int.from_bytes(proxy_addr[8:10], "big") 1219 | return (src_addr, src_port) 1220 | elif proxy_fam == PROXY2_AF_INET6: 1221 | if proxy_len >= (16 + 2)*2: 1222 | src_addr = socket.inet_ntop(socket.AF_INET6, proxy_addr[:16]) 1223 | src_port = int.from_bytes(proxy_addr[32:34], "big") 1224 | return (src_addr, src_port) 1225 | elif proxy_fam == PROXY2_AF_UNSPEC: 1226 | return peer 1227 | elif proxy_ver == 0x20: 1228 | return peer 1229 | 1230 | return False 1231 | 1232 | 1233 | async def handle_handshake(reader, writer): 1234 | global used_handshakes 1235 | global client_ips 1236 | global last_client_ips 1237 | global last_clients_with_same_handshake 1238 | 1239 | TLS_START_BYTES = b"\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03" 1240 | 1241 | if writer.transport.is_closing() or writer.get_extra_info("peername") is None: 1242 | return False 1243 | 1244 | peer = writer.get_extra_info("peername")[:2] 1245 | if not peer: 1246 | peer = ("unknown ip", 0) 1247 | 1248 | if config.PROXY_PROTOCOL: 1249 | ip = peer[0] if peer else "unknown ip" 1250 | peer = await handle_proxy_protocol(reader, peer) 1251 | if not peer: 1252 | print_err("来自客户端 %s 发送了错误的代理协议标头" % ip) 1253 | await handle_bad_client(reader, writer, None) 1254 | return False 1255 | 1256 | is_tls_handshake = True 1257 | handshake = b"" 1258 | for expected_byte in TLS_START_BYTES: 1259 | handshake += await reader.readexactly(1) 1260 | if handshake[-1] != expected_byte: 1261 | is_tls_handshake = False 1262 | break 1263 | 1264 | if is_tls_handshake: 1265 | handshake += await reader.readexactly(TLS_HANDSHAKE_LEN - len(handshake)) 1266 | tls_handshake_result = await handle_fake_tls_handshake(handshake, reader, writer, peer) 1267 | 1268 | if not tls_handshake_result: 1269 | await handle_bad_client(reader, writer, handshake) 1270 | return False 1271 | reader, writer = tls_handshake_result 1272 | handshake = await reader.readexactly(HANDSHAKE_LEN) 1273 | else: 1274 | if not config.MODES["classic"] and not config.MODES["secure"]: 1275 | await handle_bad_client(reader, writer, handshake) 1276 | return False 1277 | handshake += await reader.readexactly(HANDSHAKE_LEN - len(handshake)) 1278 | 1279 | dec_prekey_and_iv = handshake[SKIP_LEN:SKIP_LEN+PREKEY_LEN+IV_LEN] 1280 | dec_prekey, dec_iv = dec_prekey_and_iv[:PREKEY_LEN], dec_prekey_and_iv[PREKEY_LEN:] 1281 | enc_prekey_and_iv = handshake[SKIP_LEN:SKIP_LEN+PREKEY_LEN+IV_LEN][::-1] 1282 | enc_prekey, enc_iv = enc_prekey_and_iv[:PREKEY_LEN], enc_prekey_and_iv[PREKEY_LEN:] 1283 | 1284 | if dec_prekey_and_iv in used_handshakes: 1285 | last_clients_with_same_handshake[peer[0]] += 1 1286 | await handle_bad_client(reader, writer, handshake) 1287 | return False 1288 | 1289 | for user in config.USERS: 1290 | secret = bytes.fromhex(config.USERS[user]) 1291 | 1292 | dec_key = hashlib.sha256(dec_prekey + secret).digest() 1293 | decryptor = create_aes_ctr(key=dec_key, iv=int.from_bytes(dec_iv, "big")) 1294 | 1295 | enc_key = hashlib.sha256(enc_prekey + secret).digest() 1296 | encryptor = create_aes_ctr(key=enc_key, iv=int.from_bytes(enc_iv, "big")) 1297 | 1298 | decrypted = decryptor.decrypt(handshake) 1299 | 1300 | proto_tag = decrypted[PROTO_TAG_POS:PROTO_TAG_POS+4] 1301 | if proto_tag not in (PROTO_TAG_ABRIDGED, PROTO_TAG_INTERMEDIATE, PROTO_TAG_SECURE): 1302 | continue 1303 | 1304 | if proto_tag == PROTO_TAG_SECURE: 1305 | if is_tls_handshake and not config.MODES["tls"]: 1306 | continue 1307 | if not is_tls_handshake and not config.MODES["secure"]: 1308 | continue 1309 | else: 1310 | if not config.MODES["classic"]: 1311 | continue 1312 | 1313 | dc_idx = int.from_bytes(decrypted[DC_IDX_POS:DC_IDX_POS+2], "little", signed=True) 1314 | 1315 | if config.REPLAY_CHECK_LEN > 0: 1316 | while len(used_handshakes) >= config.REPLAY_CHECK_LEN: 1317 | used_handshakes.popitem(last=False) 1318 | used_handshakes[dec_prekey_and_iv] = True 1319 | 1320 | if config.CLIENT_IPS_LEN > 0: 1321 | while len(client_ips) >= config.CLIENT_IPS_LEN: 1322 | client_ips.popitem(last=False) 1323 | if peer[0] not in client_ips: 1324 | client_ips[peer[0]] = True 1325 | last_client_ips[peer[0]] = True 1326 | 1327 | reader = CryptoWrappedStreamReader(reader, decryptor) 1328 | writer = CryptoWrappedStreamWriter(writer, encryptor) 1329 | return reader, writer, proto_tag, user, dc_idx, enc_key + enc_iv, peer 1330 | 1331 | await handle_bad_client(reader, writer, handshake) 1332 | return False 1333 | 1334 | 1335 | async def do_direct_handshake(proto_tag, dc_idx, dec_key_and_iv=None): 1336 | RESERVED_NONCE_FIRST_CHARS = [b"\xef"] 1337 | RESERVED_NONCE_BEGININGS = [b"\x48\x45\x41\x44", b"\x50\x4F\x53\x54", 1338 | b"\x47\x45\x54\x20", b"\xee\xee\xee\xee", 1339 | b"\xdd\xdd\xdd\xdd", b"\x16\x03\x01\x02"] 1340 | RESERVED_NONCE_CONTINUES = [b"\x00\x00\x00\x00"] 1341 | 1342 | global my_ip_info 1343 | global tg_connection_pool 1344 | 1345 | dc_idx = abs(dc_idx) - 1 1346 | 1347 | if my_ip_info["ipv6"] and (config.PREFER_IPV6 or not my_ip_info["ipv4"]): 1348 | if not 0 <= dc_idx < len(TG_DATACENTERS_V6): 1349 | return False 1350 | dc = TG_DATACENTERS_V6[dc_idx] 1351 | else: 1352 | if not 0 <= dc_idx < len(TG_DATACENTERS_V4): 1353 | return False 1354 | dc = TG_DATACENTERS_V4[dc_idx] 1355 | 1356 | try: 1357 | reader_tgt, writer_tgt = await tg_connection_pool.get_connection(dc, TG_DATACENTER_PORT) 1358 | except ConnectionRefusedError as E: 1359 | print_err("尝试连接时被拒绝", dc, TG_DATACENTER_PORT) 1360 | return False 1361 | except ConnectionAbortedError as E: 1362 | print_err("Telegram 服务器连接不良: %d (%s %s) %s" % (dc_idx, addr, port, E)) 1363 | return False 1364 | except (OSError, asyncio.TimeoutError) as E: 1365 | print_err("无法连接到", dc, TG_DATACENTER_PORT) 1366 | return False 1367 | 1368 | while True: 1369 | rnd = bytearray(myrandom.getrandbytes(HANDSHAKE_LEN)) 1370 | if rnd[:1] in RESERVED_NONCE_FIRST_CHARS: 1371 | continue 1372 | if rnd[:4] in RESERVED_NONCE_BEGININGS: 1373 | continue 1374 | if rnd[4:8] in RESERVED_NONCE_CONTINUES: 1375 | continue 1376 | break 1377 | 1378 | rnd[PROTO_TAG_POS:PROTO_TAG_POS+4] = proto_tag 1379 | 1380 | if dec_key_and_iv: 1381 | rnd[SKIP_LEN:SKIP_LEN+KEY_LEN+IV_LEN] = dec_key_and_iv[::-1] 1382 | 1383 | rnd = bytes(rnd) 1384 | 1385 | dec_key_and_iv = rnd[SKIP_LEN:SKIP_LEN+KEY_LEN+IV_LEN][::-1] 1386 | dec_key, dec_iv = dec_key_and_iv[:KEY_LEN], dec_key_and_iv[KEY_LEN:] 1387 | decryptor = create_aes_ctr(key=dec_key, iv=int.from_bytes(dec_iv, "big")) 1388 | 1389 | enc_key_and_iv = rnd[SKIP_LEN:SKIP_LEN+KEY_LEN+IV_LEN] 1390 | enc_key, enc_iv = enc_key_and_iv[:KEY_LEN], enc_key_and_iv[KEY_LEN:] 1391 | encryptor = create_aes_ctr(key=enc_key, iv=int.from_bytes(enc_iv, "big")) 1392 | 1393 | rnd_enc = rnd[:PROTO_TAG_POS] + encryptor.encrypt(rnd)[PROTO_TAG_POS:] 1394 | 1395 | writer_tgt.write(rnd_enc) 1396 | await writer_tgt.drain() 1397 | 1398 | reader_tgt = CryptoWrappedStreamReader(reader_tgt, decryptor) 1399 | writer_tgt = CryptoWrappedStreamWriter(writer_tgt, encryptor) 1400 | 1401 | return reader_tgt, writer_tgt 1402 | 1403 | 1404 | def get_middleproxy_aes_key_and_iv(nonce_srv, nonce_clt, clt_ts, srv_ip, clt_port, purpose, 1405 | clt_ip, srv_port, middleproxy_secret, clt_ipv6=None, 1406 | srv_ipv6=None): 1407 | EMPTY_IP = b"\x00\x00\x00\x00" 1408 | 1409 | if not clt_ip or not srv_ip: 1410 | clt_ip = EMPTY_IP 1411 | srv_ip = EMPTY_IP 1412 | 1413 | s = bytearray() 1414 | s += nonce_srv + nonce_clt + clt_ts + srv_ip + clt_port + purpose + clt_ip + srv_port 1415 | s += middleproxy_secret + nonce_srv 1416 | 1417 | if clt_ipv6 and srv_ipv6: 1418 | s += clt_ipv6 + srv_ipv6 1419 | 1420 | s += nonce_clt 1421 | 1422 | md5_sum = hashlib.md5(s[1:]).digest() 1423 | sha1_sum = hashlib.sha1(s).digest() 1424 | 1425 | key = md5_sum[:12] + sha1_sum 1426 | iv = hashlib.md5(s[2:]).digest() 1427 | return key, iv 1428 | 1429 | 1430 | async def middleproxy_handshake(host, port, reader_tgt, writer_tgt): 1431 | """ The most logic of middleproxy handshake, launched in pool """ 1432 | START_SEQ_NO = -2 1433 | NONCE_LEN = 16 1434 | 1435 | RPC_HANDSHAKE = b"\xf5\xee\x82\x76" 1436 | RPC_NONCE = b"\xaa\x87\xcb\x7a" 1437 | # pass as consts to simplify code 1438 | RPC_FLAGS = b"\x00\x00\x00\x00" 1439 | CRYPTO_AES = b"\x01\x00\x00\x00" 1440 | 1441 | RPC_NONCE_ANS_LEN = 32 1442 | RPC_HANDSHAKE_ANS_LEN = 32 1443 | 1444 | writer_tgt = MTProtoFrameStreamWriter(writer_tgt, START_SEQ_NO) 1445 | key_selector = PROXY_SECRET[:4] 1446 | crypto_ts = int.to_bytes(int(time.time()) % (256**4), 4, "little") 1447 | 1448 | nonce = myrandom.getrandbytes(NONCE_LEN) 1449 | 1450 | msg = RPC_NONCE + key_selector + CRYPTO_AES + crypto_ts + nonce 1451 | 1452 | writer_tgt.write(msg) 1453 | await writer_tgt.drain() 1454 | 1455 | reader_tgt = MTProtoFrameStreamReader(reader_tgt, START_SEQ_NO) 1456 | ans = await reader_tgt.read(get_to_clt_bufsize()) 1457 | 1458 | if len(ans) != RPC_NONCE_ANS_LEN: 1459 | raise ConnectionAbortedError("bad rpc answer length") 1460 | 1461 | rpc_type, rpc_key_selector, rpc_schema, rpc_crypto_ts, rpc_nonce = ( 1462 | ans[:4], ans[4:8], ans[8:12], ans[12:16], ans[16:32] 1463 | ) 1464 | 1465 | if rpc_type != RPC_NONCE or rpc_key_selector != key_selector or rpc_schema != CRYPTO_AES: 1466 | raise ConnectionAbortedError("bad rpc answer") 1467 | 1468 | # get keys 1469 | tg_ip, tg_port = writer_tgt.upstream.get_extra_info('peername')[:2] 1470 | my_ip, my_port = writer_tgt.upstream.get_extra_info('sockname')[:2] 1471 | 1472 | use_ipv6_tg = (":" in tg_ip) 1473 | 1474 | if not use_ipv6_tg: 1475 | if my_ip_info["ipv4"]: 1476 | # prefer global ip settings to work behind NAT 1477 | my_ip = my_ip_info["ipv4"] 1478 | 1479 | tg_ip_bytes = socket.inet_pton(socket.AF_INET, tg_ip)[::-1] 1480 | my_ip_bytes = socket.inet_pton(socket.AF_INET, my_ip)[::-1] 1481 | 1482 | tg_ipv6_bytes = None 1483 | my_ipv6_bytes = None 1484 | else: 1485 | if my_ip_info["ipv6"]: 1486 | my_ip = my_ip_info["ipv6"] 1487 | 1488 | tg_ip_bytes = None 1489 | my_ip_bytes = None 1490 | 1491 | tg_ipv6_bytes = socket.inet_pton(socket.AF_INET6, tg_ip) 1492 | my_ipv6_bytes = socket.inet_pton(socket.AF_INET6, my_ip) 1493 | 1494 | tg_port_bytes = int.to_bytes(tg_port, 2, "little") 1495 | my_port_bytes = int.to_bytes(my_port, 2, "little") 1496 | 1497 | enc_key, enc_iv = get_middleproxy_aes_key_and_iv( 1498 | nonce_srv=rpc_nonce, nonce_clt=nonce, clt_ts=crypto_ts, srv_ip=tg_ip_bytes, 1499 | clt_port=my_port_bytes, purpose=b"CLIENT", clt_ip=my_ip_bytes, srv_port=tg_port_bytes, 1500 | middleproxy_secret=PROXY_SECRET, clt_ipv6=my_ipv6_bytes, srv_ipv6=tg_ipv6_bytes) 1501 | 1502 | dec_key, dec_iv = get_middleproxy_aes_key_and_iv( 1503 | nonce_srv=rpc_nonce, nonce_clt=nonce, clt_ts=crypto_ts, srv_ip=tg_ip_bytes, 1504 | clt_port=my_port_bytes, purpose=b"SERVER", clt_ip=my_ip_bytes, srv_port=tg_port_bytes, 1505 | middleproxy_secret=PROXY_SECRET, clt_ipv6=my_ipv6_bytes, srv_ipv6=tg_ipv6_bytes) 1506 | 1507 | encryptor = create_aes_cbc(key=enc_key, iv=enc_iv) 1508 | decryptor = create_aes_cbc(key=dec_key, iv=dec_iv) 1509 | 1510 | SENDER_PID = b"IPIPPRPDTIME" 1511 | PEER_PID = b"IPIPPRPDTIME" 1512 | 1513 | # TODO: pass client ip and port here for statistics 1514 | handshake = RPC_HANDSHAKE + RPC_FLAGS + SENDER_PID + PEER_PID 1515 | 1516 | writer_tgt.upstream = CryptoWrappedStreamWriter(writer_tgt.upstream, encryptor, block_size=16) 1517 | writer_tgt.write(handshake) 1518 | await writer_tgt.drain() 1519 | 1520 | reader_tgt.upstream = CryptoWrappedStreamReader(reader_tgt.upstream, decryptor, block_size=16) 1521 | 1522 | handshake_ans = await reader_tgt.read(1) 1523 | if len(handshake_ans) != RPC_HANDSHAKE_ANS_LEN: 1524 | raise ConnectionAbortedError("bad rpc handshake answer length") 1525 | 1526 | handshake_type, handshake_flags, handshake_sender_pid, handshake_peer_pid = ( 1527 | handshake_ans[:4], handshake_ans[4:8], handshake_ans[8:20], handshake_ans[20:32]) 1528 | if handshake_type != RPC_HANDSHAKE or handshake_peer_pid != SENDER_PID: 1529 | raise ConnectionAbortedError("bad rpc handshake answer") 1530 | 1531 | return reader_tgt, writer_tgt, my_ip, my_port 1532 | 1533 | 1534 | async def do_middleproxy_handshake(proto_tag, dc_idx, cl_ip, cl_port): 1535 | global my_ip_info 1536 | global tg_connection_pool 1537 | 1538 | use_ipv6_tg = (my_ip_info["ipv6"] and (config.PREFER_IPV6 or not my_ip_info["ipv4"])) 1539 | 1540 | if use_ipv6_tg: 1541 | if dc_idx not in TG_MIDDLE_PROXIES_V6: 1542 | return False 1543 | addr, port = myrandom.choice(TG_MIDDLE_PROXIES_V6[dc_idx]) 1544 | else: 1545 | if dc_idx not in TG_MIDDLE_PROXIES_V4: 1546 | return False 1547 | addr, port = myrandom.choice(TG_MIDDLE_PROXIES_V4[dc_idx]) 1548 | 1549 | try: 1550 | ret = await tg_connection_pool.get_connection(addr, port, middleproxy_handshake) 1551 | reader_tgt, writer_tgt, my_ip, my_port = ret 1552 | except ConnectionRefusedError as E: 1553 | print_err("Telegram 服务器 %d (%s %s) 拒绝连接" % (dc_idx, addr, port)) 1554 | return False 1555 | except ConnectionAbortedError as E: 1556 | print_err("Telegram 服务器连接不良: %d (%s %s) %s" % (dc_idx, addr, port, E)) 1557 | return False 1558 | except (OSError, asyncio.TimeoutError) as E: 1559 | print_err("无法连接到 Telegram 服务器 %d (%s %s)" % (dc_idx, addr, port)) 1560 | return False 1561 | 1562 | writer_tgt = ProxyReqStreamWriter(writer_tgt, cl_ip, cl_port, my_ip, my_port, proto_tag) 1563 | reader_tgt = ProxyReqStreamReader(reader_tgt) 1564 | 1565 | return reader_tgt, writer_tgt 1566 | 1567 | 1568 | async def tg_connect_reader_to_writer(rd, wr, user, rd_buf_size, is_upstream): 1569 | try: 1570 | while True: 1571 | data = await rd.read(rd_buf_size) 1572 | if isinstance(data, tuple): 1573 | data, extra = data 1574 | else: 1575 | extra = {} 1576 | 1577 | if extra.get("SKIP_SEND"): 1578 | continue 1579 | 1580 | if not data: 1581 | wr.write_eof() 1582 | await wr.drain() 1583 | return 1584 | else: 1585 | if is_upstream: 1586 | update_user_stats(user, octets_from_client=len(data), msgs_from_client=1) 1587 | else: 1588 | update_user_stats(user, octets_to_client=len(data), msgs_to_client=1) 1589 | 1590 | wr.write(data, extra) 1591 | await wr.drain() 1592 | except (OSError, asyncio.IncompleteReadError) as e: 1593 | # print_err(e) 1594 | pass 1595 | 1596 | 1597 | async def handle_client(reader_clt, writer_clt): 1598 | set_keepalive(writer_clt.get_extra_info("socket"), config.CLIENT_KEEPALIVE, attempts=3) 1599 | set_ack_timeout(writer_clt.get_extra_info("socket"), config.CLIENT_ACK_TIMEOUT) 1600 | set_bufsizes(writer_clt.get_extra_info("socket"), get_to_tg_bufsize(), get_to_clt_bufsize()) 1601 | 1602 | update_stats(connects_all=1) 1603 | 1604 | try: 1605 | clt_data = await asyncio.wait_for(handle_handshake(reader_clt, writer_clt), 1606 | timeout=config.CLIENT_HANDSHAKE_TIMEOUT) 1607 | except asyncio.TimeoutError: 1608 | update_stats(handshake_timeouts=1) 1609 | return 1610 | 1611 | if not clt_data: 1612 | return 1613 | 1614 | reader_clt, writer_clt, proto_tag, user, dc_idx, enc_key_and_iv, peer = clt_data 1615 | cl_ip, cl_port = peer 1616 | 1617 | update_user_stats(user, connects=1) 1618 | 1619 | connect_directly = (not config.USE_MIDDLE_PROXY or disable_middle_proxy) 1620 | 1621 | if connect_directly: 1622 | if config.FAST_MODE: 1623 | tg_data = await do_direct_handshake(proto_tag, dc_idx, dec_key_and_iv=enc_key_and_iv) 1624 | else: 1625 | tg_data = await do_direct_handshake(proto_tag, dc_idx) 1626 | else: 1627 | tg_data = await do_middleproxy_handshake(proto_tag, dc_idx, cl_ip, cl_port) 1628 | 1629 | if not tg_data: 1630 | return 1631 | 1632 | reader_tg, writer_tg = tg_data 1633 | 1634 | if connect_directly and config.FAST_MODE: 1635 | class FakeEncryptor: 1636 | def encrypt(self, data): 1637 | return data 1638 | 1639 | class FakeDecryptor: 1640 | def decrypt(self, data): 1641 | return data 1642 | 1643 | reader_tg.decryptor = FakeDecryptor() 1644 | writer_clt.encryptor = FakeEncryptor() 1645 | 1646 | if not connect_directly: 1647 | if proto_tag == PROTO_TAG_ABRIDGED: 1648 | reader_clt = MTProtoCompactFrameStreamReader(reader_clt) 1649 | writer_clt = MTProtoCompactFrameStreamWriter(writer_clt) 1650 | elif proto_tag == PROTO_TAG_INTERMEDIATE: 1651 | reader_clt = MTProtoIntermediateFrameStreamReader(reader_clt) 1652 | writer_clt = MTProtoIntermediateFrameStreamWriter(writer_clt) 1653 | elif proto_tag == PROTO_TAG_SECURE: 1654 | reader_clt = MTProtoSecureIntermediateFrameStreamReader(reader_clt) 1655 | writer_clt = MTProtoSecureIntermediateFrameStreamWriter(writer_clt) 1656 | else: 1657 | return 1658 | 1659 | tg_to_clt = tg_connect_reader_to_writer(reader_tg, writer_clt, user, 1660 | get_to_clt_bufsize(), False) 1661 | clt_to_tg = tg_connect_reader_to_writer(reader_clt, writer_tg, 1662 | user, get_to_tg_bufsize(), True) 1663 | task_tg_to_clt = asyncio.ensure_future(tg_to_clt) 1664 | task_clt_to_tg = asyncio.ensure_future(clt_to_tg) 1665 | 1666 | update_user_stats(user, curr_connects=1) 1667 | 1668 | tcp_limit_hit = ( 1669 | user in config.USER_MAX_TCP_CONNS and 1670 | user_stats[user]["curr_connects"] > config.USER_MAX_TCP_CONNS[user] 1671 | ) 1672 | 1673 | user_expired = ( 1674 | user in config.USER_EXPIRATIONS and 1675 | datetime.datetime.now() > config.USER_EXPIRATIONS[user] 1676 | ) 1677 | 1678 | user_data_quota_hit = ( 1679 | user in config.USER_DATA_QUOTA and 1680 | (user_stats[user]["octets_to_client"] + 1681 | user_stats[user]["octets_from_client"] > config.USER_DATA_QUOTA[user]) 1682 | ) 1683 | 1684 | if (not tcp_limit_hit) and (not user_expired) and (not user_data_quota_hit): 1685 | start = time.time() 1686 | await asyncio.wait([task_tg_to_clt, task_clt_to_tg], return_when=asyncio.FIRST_COMPLETED) 1687 | update_durations(time.time() - start) 1688 | 1689 | update_user_stats(user, curr_connects=-1) 1690 | 1691 | task_tg_to_clt.cancel() 1692 | task_clt_to_tg.cancel() 1693 | 1694 | writer_tg.transport.abort() 1695 | 1696 | 1697 | async def handle_client_wrapper(reader, writer): 1698 | try: 1699 | await handle_client(reader, writer) 1700 | except (asyncio.IncompleteReadError, asyncio.CancelledError): 1701 | pass 1702 | except (ConnectionResetError, TimeoutError, BrokenPipeError): 1703 | pass 1704 | except Exception: 1705 | traceback.print_exc() 1706 | finally: 1707 | writer.transport.abort() 1708 | 1709 | 1710 | def make_metrics_pkt(metrics): 1711 | pkt_body_list = [] 1712 | used_names = set() 1713 | 1714 | for name, m_type, desc, val in metrics: 1715 | name = config.METRICS_PREFIX + name 1716 | if name not in used_names: 1717 | pkt_body_list.append("# HELP %s %s" % (name, desc)) 1718 | pkt_body_list.append("# TYPE %s %s" % (name, m_type)) 1719 | used_names.add(name) 1720 | 1721 | if isinstance(val, dict): 1722 | tags = [] 1723 | for tag, tag_val in val.items(): 1724 | if tag == "val": 1725 | continue 1726 | tag_val = tag_val.replace('"', r'\"') 1727 | tags.append('%s="%s"' % (tag, tag_val)) 1728 | pkt_body_list.append("%s{%s} %s" % (name, ",".join(tags), val["val"])) 1729 | else: 1730 | pkt_body_list.append("%s %s" % (name, val)) 1731 | pkt_body = "\n".join(pkt_body_list) + "\n" 1732 | 1733 | pkt_header_list = [] 1734 | pkt_header_list.append("HTTP/1.1 200 OK") 1735 | pkt_header_list.append("Connection: close") 1736 | pkt_header_list.append("Content-Length: %d" % len(pkt_body)) 1737 | pkt_header_list.append("Content-Type: text/plain; version=0.0.4; charset=utf-8") 1738 | pkt_header_list.append("Date: %s" % time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())) 1739 | 1740 | pkt_header = "\r\n".join(pkt_header_list) 1741 | 1742 | pkt = pkt_header + "\r\n\r\n" + pkt_body 1743 | return pkt 1744 | 1745 | 1746 | async def handle_metrics(reader, writer): 1747 | global stats 1748 | global user_stats 1749 | global my_ip_info 1750 | global proxy_start_time 1751 | global proxy_links 1752 | global last_clients_with_time_skew 1753 | global last_clients_with_same_handshake 1754 | 1755 | client_ip = writer.get_extra_info("peername")[0] 1756 | if client_ip not in config.METRICS_WHITELIST: 1757 | writer.close() 1758 | return 1759 | 1760 | try: 1761 | metrics = [] 1762 | metrics.append(["uptime", "counter", "proxy uptime", time.time() - proxy_start_time]) 1763 | metrics.append(["connects_bad", "counter", "connects with bad secret", 1764 | stats["connects_bad"]]) 1765 | metrics.append(["connects_all", "counter", "incoming connects", stats["connects_all"]]) 1766 | metrics.append(["handshake_timeouts", "counter", "number of timed out handshakes", 1767 | stats["handshake_timeouts"]]) 1768 | 1769 | if config.METRICS_EXPORT_LINKS: 1770 | for link in proxy_links: 1771 | link_as_metric = link.copy() 1772 | link_as_metric["val"] = 1 1773 | metrics.append(["proxy_link_info", "counter", 1774 | "the proxy link info", link_as_metric]) 1775 | 1776 | bucket_start = 0 1777 | for bucket in STAT_DURATION_BUCKETS: 1778 | bucket_end = bucket if bucket != STAT_DURATION_BUCKETS[-1] else "+Inf" 1779 | metric = { 1780 | "bucket": "%s-%s" % (bucket_start, bucket_end), 1781 | "val": stats["connects_with_duration_le_%s" % str(bucket)] 1782 | } 1783 | metrics.append(["connects_by_duration", "counter", "connects by duration", metric]) 1784 | bucket_start = bucket_end 1785 | 1786 | user_metrics_desc = [ 1787 | ["user_connects", "counter", "user connects", "connects"], 1788 | ["user_connects_curr", "gauge", "current user connects", "curr_connects"], 1789 | ["user_octets", "counter", "octets proxied for user", 1790 | "octets_from_client+octets_to_client"], 1791 | ["user_msgs", "counter", "msgs proxied for user", 1792 | "msgs_from_client+msgs_to_client"], 1793 | ["user_octets_from", "counter", "octets proxied from user", "octets_from_client"], 1794 | ["user_octets_to", "counter", "octets proxied to user", "octets_to_client"], 1795 | ["user_msgs_from", "counter", "msgs proxied from user", "msgs_from_client"], 1796 | ["user_msgs_to", "counter", "msgs proxied to user", "msgs_to_client"], 1797 | ] 1798 | 1799 | for m_name, m_type, m_desc, stat_key in user_metrics_desc: 1800 | for user, stat in user_stats.items(): 1801 | if "+" in stat_key: 1802 | val = 0 1803 | for key_part in stat_key.split("+"): 1804 | val += stat[key_part] 1805 | else: 1806 | val = stat[stat_key] 1807 | metric = {"user": user, "val": val} 1808 | metrics.append([m_name, m_type, m_desc, metric]) 1809 | 1810 | pkt = make_metrics_pkt(metrics) 1811 | writer.write(pkt.encode()) 1812 | await writer.drain() 1813 | 1814 | except Exception: 1815 | traceback.print_exc() 1816 | finally: 1817 | writer.close() 1818 | 1819 | 1820 | async def stats_printer(): 1821 | global user_stats 1822 | global last_client_ips 1823 | global last_clients_with_time_skew 1824 | global last_clients_with_same_handshake 1825 | 1826 | while True: 1827 | await asyncio.sleep(config.STATS_PRINT_PERIOD) 1828 | 1829 | print("统计数据", time.strftime("%d.%m.%Y %H:%M:%S")) 1830 | for user, stat in user_stats.items(): 1831 | print("%s: %d connects (%d current), %.2f MB, %d msgs" % ( 1832 | user, stat["connects"], stat["curr_connects"], 1833 | (stat["octets_from_client"] + stat["octets_to_client"]) / 1000000, 1834 | stat["msgs_from_client"] + stat["msgs_to_client"])) 1835 | print(flush=True) 1836 | 1837 | if last_client_ips: 1838 | print("新的客户端IP:") 1839 | for ip in last_client_ips: 1840 | print(ip) 1841 | print(flush=True) 1842 | last_client_ips.clear() 1843 | 1844 | if last_clients_with_time_skew: 1845 | print("存在时间偏差的客户端(可能存在重放攻击者):") 1846 | for ip, skew_minutes in last_clients_with_time_skew.items(): 1847 | print("%s, clocks were %d minutes behind" % (ip, skew_minutes)) 1848 | print(flush=True) 1849 | last_clients_with_time_skew.clear() 1850 | if last_clients_with_same_handshake: 1851 | print("具有重复握手的客户端(可能是重放攻击者):") 1852 | for ip, times in last_clients_with_same_handshake.items(): 1853 | print("%s, %d times" % (ip, times)) 1854 | print(flush=True) 1855 | last_clients_with_same_handshake.clear() 1856 | 1857 | 1858 | async def make_https_req(url, host="core.telegram.org"): 1859 | """ Make request, return resp body and headers. """ 1860 | SSL_PORT = 443 1861 | url_data = urllib.parse.urlparse(url) 1862 | 1863 | HTTP_REQ_TEMPLATE = "\r\n".join(["GET %s HTTP/1.1", "Host: %s", 1864 | "Connection: close"]) + "\r\n\r\n" 1865 | reader, writer = await asyncio.open_connection(url_data.netloc, SSL_PORT, ssl=True) 1866 | req = HTTP_REQ_TEMPLATE % (urllib.parse.quote(url_data.path), host) 1867 | writer.write(req.encode("utf8")) 1868 | data = await reader.read() 1869 | writer.close() 1870 | 1871 | headers, body = data.split(b"\r\n\r\n", 1) 1872 | return headers, body 1873 | 1874 | 1875 | def gen_tls_client_hello_msg(server_name): 1876 | msg = bytearray() 1877 | msg += b"\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03" + myrandom.getrandbytes(32) 1878 | msg += b"\x20" + myrandom.getrandbytes(32) 1879 | msg += b"\x00\x22\x4a\x4a\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9" 1880 | msg += b"\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00\x2f\x00\x35\x00\x0a\x01\x00\x01\x91" 1881 | msg += b"\xda\xda\x00\x00\x00\x00" 1882 | msg += int.to_bytes(len(server_name) + 5, 2, "big") 1883 | msg += int.to_bytes(len(server_name) + 3, 2, "big") + b"\x00" 1884 | msg += int.to_bytes(len(server_name), 2, "big") + server_name.encode("ascii") 1885 | msg += b"\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0a\x00\x0a\x00\x08\xaa\xaa\x00\x1d\x00" 1886 | msg += b"\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00\x00\x10\x00\x0e\x00\x0c\x02" 1887 | msg += b"\x68\x32\x08\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00" 1888 | msg += b"\x00\x0d\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06" 1889 | msg += b"\x06\x01\x02\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29\xaa\xaa\x00\x01\x00\x00" 1890 | msg += b"\x1d\x00\x20" + gen_x25519_public_key() 1891 | msg += b"\x00\x2d\x00\x02\x01\x01\x00\x2b\x00\x0b\x0a\xba\xba\x03\x04\x03\x03\x03\x02\x03" 1892 | msg += b"\x01\x00\x1b\x00\x03\x02\x00\x02\x3a\x3a\x00\x01\x00\x00\x15" 1893 | msg += int.to_bytes(517 - len(msg) - 2, 2, "big") 1894 | msg += b"\x00" * (517 - len(msg)) 1895 | return bytes(msg) 1896 | 1897 | 1898 | async def get_encrypted_cert(host, port, server_name): 1899 | async def get_tls_record(reader): 1900 | try: 1901 | record_type = (await reader.readexactly(1))[0] 1902 | tls_version = await reader.readexactly(2) 1903 | if tls_version != b"\x03\x03": 1904 | return 0, b"" 1905 | record_len = int.from_bytes(await reader.readexactly(2), "big") 1906 | record = await reader.readexactly(record_len) 1907 | 1908 | return record_type, record 1909 | except asyncio.IncompleteReadError: 1910 | return 0, b"" 1911 | 1912 | reader, writer = await asyncio.open_connection(host, port) 1913 | writer.write(gen_tls_client_hello_msg(server_name)) 1914 | await writer.drain() 1915 | 1916 | record1_type, record1 = await get_tls_record(reader) 1917 | if record1_type != 22: 1918 | return b"" 1919 | 1920 | record2_type, record2 = await get_tls_record(reader) 1921 | if record2_type != 20: 1922 | return b"" 1923 | 1924 | record3_type, record3 = await get_tls_record(reader) 1925 | if record3_type != 23: 1926 | return b"" 1927 | 1928 | if len(record3) < MIN_CERT_LEN: 1929 | record4_type, record4 = await get_tls_record(reader) 1930 | if record4_type != 23: 1931 | return b"" 1932 | msg = ("此域名 %s 在证书记录之前发送了一些 TLS 记录," + 1933 | "代理可能更易被检测到") % config.MASK_HOST 1934 | print_err(msg) 1935 | 1936 | return record4 1937 | 1938 | return record3 1939 | 1940 | 1941 | async def get_mask_host_cert_len(): 1942 | global fake_cert_len 1943 | 1944 | GET_CERT_TIMEOUT = 10 1945 | MASK_ENABLING_CHECK_PERIOD = 60 1946 | 1947 | while True: 1948 | try: 1949 | if not config.MASK: 1950 | # do nothing 1951 | await asyncio.sleep(MASK_ENABLING_CHECK_PERIOD) 1952 | continue 1953 | 1954 | task = get_encrypted_cert(config.MASK_HOST, config.MASK_PORT, config.TLS_DOMAIN) 1955 | cert = await asyncio.wait_for(task, timeout=GET_CERT_TIMEOUT) 1956 | if cert: 1957 | if len(cert) < MIN_CERT_LEN: 1958 | msg = ("The MASK_HOST %s 返回了多条 TLS 记录,这是不支持的" % 1959 | config.MASK_HOST) 1960 | print_err(msg) 1961 | elif len(cert) != fake_cert_len: 1962 | fake_cert_len = len(cert) 1963 | print_err("获得证书 %s, 它的长度是 %d" % 1964 | (config.MASK_HOST, fake_cert_len)) 1965 | else: 1966 | print_err("The MASK_HOST %s 不是 TLS 1.3 主机,不建议这样做" % 1967 | config.MASK_HOST) 1968 | except ConnectionRefusedError: 1969 | print_err("The MASK_HOST %s 拒绝连接,不建议这样做" % 1970 | config.MASK_HOST) 1971 | except (TimeoutError, asyncio.TimeoutError): 1972 | print_err("获取 TLS 握手时超时 MASK_HOST %s" % 1973 | config.MASK_HOST) 1974 | except Exception as E: 1975 | print_err("无法连接到 MASK_HOST %s: %s" % ( 1976 | config.MASK_HOST, E)) 1977 | 1978 | await asyncio.sleep(config.GET_CERT_LEN_PERIOD) 1979 | 1980 | 1981 | async def get_srv_time(): 1982 | TIME_SYNC_ADDR = "https://core.telegram.org/getProxySecret" 1983 | MAX_TIME_SKEW = 30 1984 | 1985 | global disable_middle_proxy 1986 | global is_time_skewed 1987 | 1988 | want_to_reenable_advertising = False 1989 | while True: 1990 | try: 1991 | headers, secret = await make_https_req(TIME_SYNC_ADDR) 1992 | 1993 | for line in headers.split(b"\r\n"): 1994 | if not line.startswith(b"Date: "): 1995 | continue 1996 | line = line[len("Date: "):].decode() 1997 | srv_time = datetime.datetime.strptime(line, "%a, %d %b %Y %H:%M:%S %Z") 1998 | now_time = datetime.datetime.utcnow() 1999 | is_time_skewed = (now_time-srv_time).total_seconds() > MAX_TIME_SKEW 2000 | if is_time_skewed and config.USE_MIDDLE_PROXY and not disable_middle_proxy: 2001 | print_err("检测到时间偏差,请更新系统时间") 2002 | print_err("Server time:", srv_time, "your time:", now_time) 2003 | print_err("停用广告以继续投放") 2004 | print_err("放下防御重放攻击的盾牌") 2005 | 2006 | disable_middle_proxy = True 2007 | want_to_reenable_advertising = True 2008 | elif not is_time_skewed and want_to_reenable_advertising: 2009 | print_err("系统时间正确,重新启用广告") 2010 | disable_middle_proxy = False 2011 | want_to_reenable_advertising = False 2012 | except Exception as E: 2013 | print_err("获取服务器时间错误", E) 2014 | 2015 | await asyncio.sleep(config.GET_TIME_PERIOD) 2016 | 2017 | 2018 | async def clear_ip_resolving_cache(): 2019 | global mask_host_cached_ip 2020 | min_sleep = myrandom.randrange(60 - 10, 60 + 10) 2021 | max_sleep = myrandom.randrange(120 - 10, 120 + 10) 2022 | while True: 2023 | mask_host_cached_ip = None 2024 | await asyncio.sleep(myrandom.randrange(min_sleep, max_sleep)) 2025 | 2026 | 2027 | async def update_middle_proxy_info(): 2028 | async def get_new_proxies(url): 2029 | PROXY_REGEXP = re.compile(r"proxy_for\s+(-?\d+)\s+(.+):(\d+)\s*;") 2030 | ans = {} 2031 | headers, body = await make_https_req(url) 2032 | 2033 | fields = PROXY_REGEXP.findall(body.decode("utf8")) 2034 | if fields: 2035 | for dc_idx, host, port in fields: 2036 | if host.startswith("[") and host.endswith("]"): 2037 | host = host[1:-1] 2038 | dc_idx, port = int(dc_idx), int(port) 2039 | if dc_idx not in ans: 2040 | ans[dc_idx] = [(host, port)] 2041 | else: 2042 | ans[dc_idx].append((host, port)) 2043 | return ans 2044 | 2045 | PROXY_INFO_ADDR = "https://core.telegram.org/getProxyConfig" 2046 | PROXY_INFO_ADDR_V6 = "https://core.telegram.org/getProxyConfigV6" 2047 | PROXY_SECRET_ADDR = "https://core.telegram.org/getProxySecret" 2048 | 2049 | global TG_MIDDLE_PROXIES_V4 2050 | global TG_MIDDLE_PROXIES_V6 2051 | global PROXY_SECRET 2052 | 2053 | while True: 2054 | try: 2055 | v4_proxies = await get_new_proxies(PROXY_INFO_ADDR) 2056 | if not v4_proxies: 2057 | raise Exception("no proxy data") 2058 | TG_MIDDLE_PROXIES_V4 = v4_proxies 2059 | except Exception as E: 2060 | print_err("更新中间代理列表时出错:", E) 2061 | 2062 | try: 2063 | v6_proxies = await get_new_proxies(PROXY_INFO_ADDR_V6) 2064 | if not v6_proxies: 2065 | raise Exception("no proxy data (ipv6)") 2066 | TG_MIDDLE_PROXIES_V6 = v6_proxies 2067 | except Exception as E: 2068 | print_err("Error updating middle proxy list for IPv6:", E) 2069 | 2070 | try: 2071 | headers, secret = await make_https_req(PROXY_SECRET_ADDR) 2072 | if not secret: 2073 | raise Exception("no secret") 2074 | if secret != PROXY_SECRET: 2075 | PROXY_SECRET = secret 2076 | print_err("中间代理密钥已更新") 2077 | except Exception as E: 2078 | print_err("更新中间代理密钥时出错,使用旧的", E) 2079 | 2080 | await asyncio.sleep(config.PROXY_INFO_UPDATE_PERIOD) 2081 | 2082 | 2083 | def init_ip_info(): 2084 | global my_ip_info 2085 | global disable_middle_proxy 2086 | 2087 | def get_ip_from_url(url): 2088 | TIMEOUT = 5 2089 | try: 2090 | with urllib.request.urlopen(url, timeout=TIMEOUT) as f: 2091 | if f.status != 200: 2092 | raise Exception("Invalid status code") 2093 | return f.read().decode().strip() 2094 | except Exception: 2095 | return None 2096 | 2097 | IPV4_URL1 = "http://v4.ident.me/" 2098 | IPV4_URL2 = "http://ipv4.icanhazip.com/" 2099 | 2100 | IPV6_URL1 = "http://v6.ident.me/" 2101 | IPV6_URL2 = "http://ipv6.icanhazip.com/" 2102 | 2103 | my_ip_info["ipv4"] = get_ip_from_url(IPV4_URL1) or get_ip_from_url(IPV4_URL2) 2104 | my_ip_info["ipv6"] = get_ip_from_url(IPV6_URL1) or get_ip_from_url(IPV6_URL2) 2105 | 2106 | # the server can return ipv4 address instead of ipv6 2107 | if my_ip_info["ipv6"] and ":" not in my_ip_info["ipv6"]: 2108 | my_ip_info["ipv6"] = None 2109 | 2110 | if my_ip_info["ipv6"] and (config.PREFER_IPV6 or not my_ip_info["ipv4"]): 2111 | print_err("发现 IPv6,正在使用它进行外部通信") 2112 | 2113 | if config.USE_MIDDLE_PROXY: 2114 | if not my_ip_info["ipv4"] and not my_ip_info["ipv6"]: 2115 | print_err("无法确定您的 IP,广告已禁用") 2116 | disable_middle_proxy = True 2117 | 2118 | 2119 | def print_tg_info(): 2120 | global my_ip_info 2121 | global proxy_links 2122 | 2123 | print_default_warning = False 2124 | 2125 | if config.PORT == 3256: 2126 | print("使用了默认端口3256,不推荐使用", flush=True) 2127 | if not config.MODES["classic"] and not config.MODES["secure"]: 2128 | print("由于您启用了仅 TLS 模式,因此最佳端口是 443", flush=True) 2129 | print_default_warning = True 2130 | 2131 | if not config.MY_DOMAIN: 2132 | ip_addrs = [ip for ip in my_ip_info.values() if ip] 2133 | if not ip_addrs: 2134 | ip_addrs = ["YOUR_IP"] 2135 | else: 2136 | ip_addrs = [config.MY_DOMAIN] 2137 | 2138 | proxy_links = [] 2139 | 2140 | for user, secret in sorted(config.USERS.items(), key=lambda x: x[0]): 2141 | for ip in ip_addrs: 2142 | if config.MODES["classic"]: 2143 | params = {"server": ip, "port": config.PORT, "secret": secret} 2144 | params_encodeded = urllib.parse.urlencode(params, safe=':') 2145 | classic_link = "tg://proxy?{}".format(params_encodeded) 2146 | proxy_links.append({"user": user, "link": classic_link}) 2147 | print("{}: {}".format(user, classic_link), flush=True) 2148 | 2149 | if config.MODES["secure"]: 2150 | params = {"server": ip, "port": config.PORT, "secret": "dd" + secret} 2151 | params_encodeded = urllib.parse.urlencode(params, safe=':') 2152 | dd_link = "tg://proxy?{}".format(params_encodeded) 2153 | proxy_links.append({"user": user, "link": dd_link}) 2154 | print("{}: {}".format(user, dd_link), flush=True) 2155 | 2156 | if config.MODES["tls"]: 2157 | tls_secret = "ee" + secret + config.TLS_DOMAIN.encode().hex() 2158 | # the base64 links is buggy on ios 2159 | # tls_secret = bytes.fromhex("ee" + secret) + config.TLS_DOMAIN.encode() 2160 | # tls_secret_base64 = base64.urlsafe_b64encode(tls_secret) 2161 | params = {"server": ip, "port": config.PORT, "secret": tls_secret} 2162 | params_encodeded = urllib.parse.urlencode(params, safe=':') 2163 | tls_link = "tg://proxy?{}".format(params_encodeded) 2164 | proxy_links.append({"user": user, "link": tls_link}) 2165 | print("{}: {}".format(user, tls_link), flush=True) 2166 | 2167 | if secret in ["00000000000000000000000000000000", "bc6dddb180509c5b18cb51d17d849455", 2168 | "fa33e8ed3523ce760c241129c66153b6"]: 2169 | msg = "使用默认密钥 {},不推荐使用".format(secret) 2170 | print(msg, flush=True) 2171 | random_secret = "".join(myrandom.choice("0123456789abcdef") for i in range(32)) 2172 | print("您可以将其更改为这个随机密钥:", random_secret, flush=True) 2173 | print_default_warning = True 2174 | 2175 | if config.TLS_DOMAIN == "www.swift.com": 2176 | print("使用了默认域名 www.swift.com", flush=True) 2177 | msg = "你应该使用随机的现有域名,这样才能减少被墙的几率" 2178 | print(msg, flush=True) 2179 | print_default_warning = True 2180 | 2181 | if print_default_warning: 2182 | print_err("警告:检测到一个或多个默认设置") 2183 | 2184 | 2185 | def setup_files_limit(): 2186 | try: 2187 | import resource 2188 | soft_fd_limit, hard_fd_limit = resource.getrlimit(resource.RLIMIT_NOFILE) 2189 | resource.setrlimit(resource.RLIMIT_NOFILE, (hard_fd_limit, hard_fd_limit)) 2190 | except (ValueError, OSError): 2191 | print("Failed to increase the limit of opened files", flush=True, file=sys.stderr) 2192 | except ImportError: 2193 | pass 2194 | 2195 | 2196 | def setup_asyncio(): 2197 | # get rid of annoying "socket.send() raised exception" log messages 2198 | asyncio.constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES = 100 2199 | 2200 | 2201 | def setup_signals(): 2202 | if hasattr(signal, 'SIGUSR1'): 2203 | def debug_signal(signum, frame): 2204 | import pdb 2205 | pdb.set_trace() 2206 | 2207 | signal.signal(signal.SIGUSR1, debug_signal) 2208 | 2209 | if hasattr(signal, 'SIGUSR2'): 2210 | def reload_signal(signum, frame): 2211 | init_config() 2212 | ensure_users_in_user_stats() 2213 | apply_upstream_proxy_settings() 2214 | print("Config reloaded", flush=True, file=sys.stderr) 2215 | print_tg_info() 2216 | 2217 | signal.signal(signal.SIGUSR2, reload_signal) 2218 | 2219 | 2220 | def try_setup_uvloop(): 2221 | if config.SOCKS5_HOST and config.SOCKS5_PORT: 2222 | # socks mode is not compatible with uvloop 2223 | return 2224 | try: 2225 | import uvloop 2226 | asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) 2227 | print_err("找到 uvloop,使用它获得最佳性能") 2228 | except ImportError: 2229 | pass 2230 | 2231 | 2232 | def remove_unix_socket(path): 2233 | try: 2234 | if stat.S_ISSOCK(os.stat(path).st_mode): 2235 | os.unlink(path) 2236 | except (FileNotFoundError, NotADirectoryError): 2237 | pass 2238 | 2239 | 2240 | def loop_exception_handler(loop, context): 2241 | exception = context.get("exception") 2242 | transport = context.get("transport") 2243 | if exception: 2244 | if isinstance(exception, TimeoutError): 2245 | if transport: 2246 | transport.abort() 2247 | return 2248 | if isinstance(exception, OSError): 2249 | IGNORE_ERRNO = { 2250 | 10038, # operation on non-socket on Windows, likely because fd == -1 2251 | 121, # the semaphore timeout period has expired on Windows 2252 | } 2253 | 2254 | FORCE_CLOSE_ERRNO = { 2255 | 113, # no route to host 2256 | 2257 | } 2258 | if exception.errno in IGNORE_ERRNO: 2259 | return 2260 | elif exception.errno in FORCE_CLOSE_ERRNO: 2261 | if transport: 2262 | transport.abort() 2263 | return 2264 | 2265 | loop.default_exception_handler(context) 2266 | 2267 | 2268 | def create_servers(loop): 2269 | servers = [] 2270 | 2271 | reuse_port = hasattr(socket, "SO_REUSEPORT") 2272 | has_unix = hasattr(socket, "AF_UNIX") 2273 | 2274 | if config.LISTEN_ADDR_IPV4: 2275 | task = asyncio.start_server(handle_client_wrapper, config.LISTEN_ADDR_IPV4, config.PORT, 2276 | limit=get_to_tg_bufsize(), reuse_port=reuse_port) 2277 | servers.append(loop.run_until_complete(task)) 2278 | 2279 | if config.LISTEN_ADDR_IPV6 and socket.has_ipv6: 2280 | task = asyncio.start_server(handle_client_wrapper, config.LISTEN_ADDR_IPV6, config.PORT, 2281 | limit=get_to_tg_bufsize(), reuse_port=reuse_port) 2282 | servers.append(loop.run_until_complete(task)) 2283 | 2284 | if config.LISTEN_UNIX_SOCK and has_unix: 2285 | remove_unix_socket(config.LISTEN_UNIX_SOCK) 2286 | task = asyncio.start_unix_server(handle_client_wrapper, config.LISTEN_UNIX_SOCK, 2287 | limit=get_to_tg_bufsize()) 2288 | servers.append(loop.run_until_complete(task)) 2289 | os.chmod(config.LISTEN_UNIX_SOCK, 0o666) 2290 | 2291 | if config.METRICS_PORT is not None: 2292 | if config.METRICS_LISTEN_ADDR_IPV4: 2293 | task = asyncio.start_server(handle_metrics, config.METRICS_LISTEN_ADDR_IPV4, 2294 | config.METRICS_PORT) 2295 | servers.append(loop.run_until_complete(task)) 2296 | if config.METRICS_LISTEN_ADDR_IPV6 and socket.has_ipv6: 2297 | task = asyncio.start_server(handle_metrics, config.METRICS_LISTEN_ADDR_IPV6, 2298 | config.METRICS_PORT) 2299 | servers.append(loop.run_until_complete(task)) 2300 | 2301 | return servers 2302 | 2303 | 2304 | def create_utilitary_tasks(loop): 2305 | tasks = [] 2306 | 2307 | stats_printer_task = asyncio.Task(stats_printer(), loop=loop) 2308 | tasks.append(stats_printer_task) 2309 | 2310 | if config.USE_MIDDLE_PROXY: 2311 | middle_proxy_updater_task = asyncio.Task(update_middle_proxy_info(), loop=loop) 2312 | tasks.append(middle_proxy_updater_task) 2313 | 2314 | if config.GET_TIME_PERIOD: 2315 | time_get_task = asyncio.Task(get_srv_time(), loop=loop) 2316 | tasks.append(time_get_task) 2317 | 2318 | get_cert_len_task = asyncio.Task(get_mask_host_cert_len(), loop=loop) 2319 | tasks.append(get_cert_len_task) 2320 | 2321 | clear_resolving_cache_task = asyncio.Task(clear_ip_resolving_cache(), loop=loop) 2322 | tasks.append(clear_resolving_cache_task) 2323 | 2324 | return tasks 2325 | 2326 | 2327 | def main(): 2328 | init_config() 2329 | ensure_users_in_user_stats() 2330 | apply_upstream_proxy_settings() 2331 | init_ip_info() 2332 | print_tg_info() 2333 | 2334 | setup_asyncio() 2335 | setup_files_limit() 2336 | setup_signals() 2337 | try_setup_uvloop() 2338 | 2339 | init_proxy_start_time() 2340 | 2341 | if sys.platform == "win32": 2342 | loop = asyncio.ProactorEventLoop() 2343 | else: 2344 | loop = asyncio.new_event_loop() 2345 | 2346 | asyncio.set_event_loop(loop) 2347 | loop.set_exception_handler(loop_exception_handler) 2348 | 2349 | utilitary_tasks = create_utilitary_tasks(loop) 2350 | for task in utilitary_tasks: 2351 | asyncio.ensure_future(task) 2352 | 2353 | servers = create_servers(loop) 2354 | 2355 | try: 2356 | loop.run_forever() 2357 | except KeyboardInterrupt: 2358 | pass 2359 | 2360 | if hasattr(asyncio, "all_tasks"): 2361 | tasks = asyncio.all_tasks(loop) 2362 | else: 2363 | # for compatibility with Python 3.6 2364 | tasks = asyncio.Task.all_tasks(loop) 2365 | 2366 | for task in tasks: 2367 | task.cancel() 2368 | 2369 | for server in servers: 2370 | server.close() 2371 | loop.run_until_complete(server.wait_closed()) 2372 | 2373 | has_unix = hasattr(socket, "AF_UNIX") 2374 | 2375 | if config.LISTEN_UNIX_SOCK and has_unix: 2376 | remove_unix_socket(config.LISTEN_UNIX_SOCK) 2377 | 2378 | loop.close() 2379 | 2380 | 2381 | if __name__ == "__main__": 2382 | main() 2383 | -------------------------------------------------------------------------------- /pyaes/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # This is a pure-Python implementation of the AES algorithm and AES common 24 | # modes of operation. 25 | 26 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard 27 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation 28 | 29 | 30 | # Supported key sizes: 31 | # 128-bit 32 | # 192-bit 33 | # 256-bit 34 | 35 | 36 | # Supported modes of operation: 37 | # ECB - Electronic Codebook 38 | # CBC - Cipher-Block Chaining 39 | # CFB - Cipher Feedback 40 | # OFB - Output Feedback 41 | # CTR - Counter 42 | 43 | # See the README.md for API details and general information. 44 | 45 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: 46 | # https://www.dlitz.net/software/pycrypto/ 47 | 48 | 49 | VERSION = [1, 3, 0] 50 | 51 | from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter 52 | from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter 53 | from .blockfeeder import PADDING_NONE, PADDING_DEFAULT 54 | -------------------------------------------------------------------------------- /pyaes/aes.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # This is a pure-Python implementation of the AES algorithm and AES common 24 | # modes of operation. 25 | 26 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard 27 | 28 | # Honestly, the best description of the modes of operations are the wonderful 29 | # diagrams on Wikipedia. They explain in moments what my words could never 30 | # achieve. Hence the inline documentation here is sparer than I'd prefer. 31 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation 32 | 33 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: 34 | # https://www.dlitz.net/software/pycrypto/ 35 | 36 | 37 | # Supported key sizes: 38 | # 128-bit 39 | # 192-bit 40 | # 256-bit 41 | 42 | 43 | # Supported modes of operation: 44 | # ECB - Electronic Codebook 45 | # CBC - Cipher-Block Chaining 46 | # CFB - Cipher Feedback 47 | # OFB - Output Feedback 48 | # CTR - Counter 49 | 50 | 51 | # See the README.md for API details and general information. 52 | 53 | 54 | import copy 55 | import struct 56 | 57 | __all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB", 58 | "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"] 59 | 60 | 61 | def _compact_word(word): 62 | return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] 63 | 64 | def _string_to_bytes(text): 65 | return list(ord(c) for c in text) 66 | 67 | def _bytes_to_string(binary): 68 | return "".join(chr(b) for b in binary) 69 | 70 | def _concat_list(a, b): 71 | return a + b 72 | 73 | 74 | # Python 3 compatibility 75 | try: 76 | xrange 77 | except Exception: 78 | xrange = range 79 | 80 | # Python 3 supports bytes, which is already an array of integers 81 | def _string_to_bytes(text): 82 | if isinstance(text, bytes): 83 | return text 84 | return [ord(c) for c in text] 85 | 86 | # In Python 3, we return bytes 87 | def _bytes_to_string(binary): 88 | return bytes(binary) 89 | 90 | # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first 91 | def _concat_list(a, b): 92 | return a + bytes(b) 93 | 94 | 95 | # Based *largely* on the Rijndael implementation 96 | # See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf 97 | class AES(object): 98 | '''Encapsulates the AES block cipher. 99 | 100 | You generally should not need this. Use the AESModeOfOperation classes 101 | below instead.''' 102 | 103 | # Number of rounds by keysize 104 | number_of_rounds = {16: 10, 24: 12, 32: 14} 105 | 106 | # Round constant words 107 | rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] 108 | 109 | # S-box and Inverse S-box (S is for Substitution) 110 | S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] 111 | Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] 112 | 113 | # Transformations for encryption 114 | T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] 115 | T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] 116 | T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] 117 | T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] 118 | 119 | # Transformations for decryption 120 | T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] 121 | T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] 122 | T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] 123 | T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] 124 | 125 | # Transformations for decryption key expansion 126 | U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] 127 | U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] 128 | U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] 129 | U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] 130 | 131 | def __init__(self, key): 132 | 133 | if len(key) not in (16, 24, 32): 134 | raise ValueError('Invalid key size') 135 | 136 | rounds = self.number_of_rounds[len(key)] 137 | 138 | # Encryption round keys 139 | self._Ke = [[0] * 4 for i in xrange(rounds + 1)] 140 | 141 | # Decryption round keys 142 | self._Kd = [[0] * 4 for i in xrange(rounds + 1)] 143 | 144 | round_key_count = (rounds + 1) * 4 145 | KC = len(key) // 4 146 | 147 | # Convert the key into ints 148 | tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] 149 | 150 | # Copy values into round key arrays 151 | for i in xrange(0, KC): 152 | self._Ke[i // 4][i % 4] = tk[i] 153 | self._Kd[rounds - (i // 4)][i % 4] = tk[i] 154 | 155 | # Key expansion (fips-197 section 5.2) 156 | rconpointer = 0 157 | t = KC 158 | while t < round_key_count: 159 | 160 | tt = tk[KC - 1] 161 | tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ 162 | (self.S[(tt >> 8) & 0xFF] << 16) ^ 163 | (self.S[ tt & 0xFF] << 8) ^ 164 | self.S[(tt >> 24) & 0xFF] ^ 165 | (self.rcon[rconpointer] << 24)) 166 | rconpointer += 1 167 | 168 | if KC != 8: 169 | for i in xrange(1, KC): 170 | tk[i] ^= tk[i - 1] 171 | 172 | # Key expansion for 256-bit keys is "slightly different" (fips-197) 173 | else: 174 | for i in xrange(1, KC // 2): 175 | tk[i] ^= tk[i - 1] 176 | tt = tk[KC // 2 - 1] 177 | 178 | tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ 179 | (self.S[(tt >> 8) & 0xFF] << 8) ^ 180 | (self.S[(tt >> 16) & 0xFF] << 16) ^ 181 | (self.S[(tt >> 24) & 0xFF] << 24)) 182 | 183 | for i in xrange(KC // 2 + 1, KC): 184 | tk[i] ^= tk[i - 1] 185 | 186 | # Copy values into round key arrays 187 | j = 0 188 | while j < KC and t < round_key_count: 189 | self._Ke[t // 4][t % 4] = tk[j] 190 | self._Kd[rounds - (t // 4)][t % 4] = tk[j] 191 | j += 1 192 | t += 1 193 | 194 | # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) 195 | for r in xrange(1, rounds): 196 | for j in xrange(0, 4): 197 | tt = self._Kd[r][j] 198 | self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ 199 | self.U2[(tt >> 16) & 0xFF] ^ 200 | self.U3[(tt >> 8) & 0xFF] ^ 201 | self.U4[ tt & 0xFF]) 202 | 203 | def encrypt(self, plaintext): 204 | 'Encrypt a block of plain text using the AES block cipher.' 205 | 206 | if len(plaintext) != 16: 207 | raise ValueError('wrong block length') 208 | 209 | rounds = len(self._Ke) - 1 210 | (s1, s2, s3) = [1, 2, 3] 211 | a = [0, 0, 0, 0] 212 | 213 | # Convert plaintext to (ints ^ key) 214 | t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] 215 | 216 | # Apply round transforms 217 | for r in xrange(1, rounds): 218 | for i in xrange(0, 4): 219 | a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ 220 | self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ 221 | self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ 222 | self.T4[ t[(i + s3) % 4] & 0xFF] ^ 223 | self._Ke[r][i]) 224 | t = copy.copy(a) 225 | 226 | # The last round is special 227 | result = [ ] 228 | for i in xrange(0, 4): 229 | tt = self._Ke[rounds][i] 230 | result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) 231 | result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) 232 | result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) 233 | result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) 234 | 235 | return result 236 | 237 | def decrypt(self, ciphertext): 238 | 'Decrypt a block of cipher text using the AES block cipher.' 239 | 240 | if len(ciphertext) != 16: 241 | raise ValueError('wrong block length') 242 | 243 | rounds = len(self._Kd) - 1 244 | (s1, s2, s3) = [3, 2, 1] 245 | a = [0, 0, 0, 0] 246 | 247 | # Convert ciphertext to (ints ^ key) 248 | t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] 249 | 250 | # Apply round transforms 251 | for r in xrange(1, rounds): 252 | for i in xrange(0, 4): 253 | a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ 254 | self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ 255 | self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ 256 | self.T8[ t[(i + s3) % 4] & 0xFF] ^ 257 | self._Kd[r][i]) 258 | t = copy.copy(a) 259 | 260 | # The last round is special 261 | result = [ ] 262 | for i in xrange(0, 4): 263 | tt = self._Kd[rounds][i] 264 | result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) 265 | result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) 266 | result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) 267 | result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) 268 | 269 | return result 270 | 271 | 272 | class Counter(object): 273 | '''A counter object for the Counter (CTR) mode of operation. 274 | 275 | To create a custom counter, you can usually just override the 276 | increment method.''' 277 | 278 | def __init__(self, initial_value = 1): 279 | 280 | # Convert the value into an array of bytes long 281 | self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ] 282 | 283 | value = property(lambda s: s._counter) 284 | 285 | def increment(self): 286 | '''Increment the counter (overflow rolls back to 0).''' 287 | 288 | for i in xrange(len(self._counter) - 1, -1, -1): 289 | self._counter[i] += 1 290 | 291 | if self._counter[i] < 256: break 292 | 293 | # Carry the one 294 | self._counter[i] = 0 295 | 296 | # Overflow 297 | else: 298 | self._counter = [ 0 ] * len(self._counter) 299 | 300 | 301 | class AESBlockModeOfOperation(object): 302 | '''Super-class for AES modes of operation that require blocks.''' 303 | def __init__(self, key): 304 | self._aes = AES(key) 305 | 306 | def decrypt(self, ciphertext): 307 | raise Exception('not implemented') 308 | 309 | def encrypt(self, plaintext): 310 | raise Exception('not implemented') 311 | 312 | 313 | class AESStreamModeOfOperation(AESBlockModeOfOperation): 314 | '''Super-class for AES modes of operation that are stream-ciphers.''' 315 | 316 | class AESSegmentModeOfOperation(AESStreamModeOfOperation): 317 | '''Super-class for AES modes of operation that segment data.''' 318 | 319 | segment_bytes = 16 320 | 321 | 322 | 323 | class AESModeOfOperationECB(AESBlockModeOfOperation): 324 | '''AES Electronic Codebook Mode of Operation. 325 | 326 | o Block-cipher, so data must be padded to 16 byte boundaries 327 | 328 | Security Notes: 329 | o This mode is not recommended 330 | o Any two identical blocks produce identical encrypted values, 331 | exposing data patterns. (See the image of Tux on wikipedia) 332 | 333 | Also see: 334 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 335 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1''' 336 | 337 | 338 | name = "Electronic Codebook (ECB)" 339 | 340 | def encrypt(self, plaintext): 341 | if len(plaintext) != 16: 342 | raise ValueError('plaintext block must be 16 bytes') 343 | 344 | plaintext = _string_to_bytes(plaintext) 345 | return _bytes_to_string(self._aes.encrypt(plaintext)) 346 | 347 | def decrypt(self, ciphertext): 348 | if len(ciphertext) != 16: 349 | raise ValueError('ciphertext block must be 16 bytes') 350 | 351 | ciphertext = _string_to_bytes(ciphertext) 352 | return _bytes_to_string(self._aes.decrypt(ciphertext)) 353 | 354 | 355 | 356 | class AESModeOfOperationCBC(AESBlockModeOfOperation): 357 | '''AES Cipher-Block Chaining Mode of Operation. 358 | 359 | o The Initialization Vector (IV) 360 | o Block-cipher, so data must be padded to 16 byte boundaries 361 | o An incorrect initialization vector will only cause the first 362 | block to be corrupt; all other blocks will be intact 363 | o A corrupt bit in the cipher text will cause a block to be 364 | corrupted, and the next block to be inverted, but all other 365 | blocks will be intact. 366 | 367 | Security Notes: 368 | o This method (and CTR) ARE recommended. 369 | 370 | Also see: 371 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 372 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2''' 373 | 374 | 375 | name = "Cipher-Block Chaining (CBC)" 376 | 377 | def __init__(self, key, iv = None): 378 | if iv is None: 379 | self._last_cipherblock = [ 0 ] * 16 380 | elif len(iv) != 16: 381 | raise ValueError('initialization vector must be 16 bytes') 382 | else: 383 | self._last_cipherblock = _string_to_bytes(iv) 384 | 385 | AESBlockModeOfOperation.__init__(self, key) 386 | 387 | def encrypt(self, plaintext): 388 | if len(plaintext) != 16: 389 | raise ValueError('plaintext block must be 16 bytes') 390 | 391 | plaintext = _string_to_bytes(plaintext) 392 | precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] 393 | self._last_cipherblock = self._aes.encrypt(precipherblock) 394 | 395 | return _bytes_to_string(self._last_cipherblock) 396 | 397 | def decrypt(self, ciphertext): 398 | if len(ciphertext) != 16: 399 | raise ValueError('ciphertext block must be 16 bytes') 400 | 401 | cipherblock = _string_to_bytes(ciphertext) 402 | plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] 403 | self._last_cipherblock = cipherblock 404 | 405 | return _bytes_to_string(plaintext) 406 | 407 | 408 | 409 | class AESModeOfOperationCFB(AESSegmentModeOfOperation): 410 | '''AES Cipher Feedback Mode of Operation. 411 | 412 | o A stream-cipher, so input does not need to be padded to blocks, 413 | but does need to be padded to segment_size 414 | 415 | Also see: 416 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29 417 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3''' 418 | 419 | 420 | name = "Cipher Feedback (CFB)" 421 | 422 | def __init__(self, key, iv, segment_size = 1): 423 | if segment_size == 0: segment_size = 1 424 | 425 | if iv is None: 426 | self._shift_register = [ 0 ] * 16 427 | elif len(iv) != 16: 428 | raise ValueError('initialization vector must be 16 bytes') 429 | else: 430 | self._shift_register = _string_to_bytes(iv) 431 | 432 | self._segment_bytes = segment_size 433 | 434 | AESBlockModeOfOperation.__init__(self, key) 435 | 436 | segment_bytes = property(lambda s: s._segment_bytes) 437 | 438 | def encrypt(self, plaintext): 439 | if len(plaintext) % self._segment_bytes != 0: 440 | raise ValueError('plaintext block must be a multiple of segment_size') 441 | 442 | plaintext = _string_to_bytes(plaintext) 443 | 444 | # Break block into segments 445 | encrypted = [ ] 446 | for i in xrange(0, len(plaintext), self._segment_bytes): 447 | plaintext_segment = plaintext[i: i + self._segment_bytes] 448 | xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)] 449 | cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ] 450 | 451 | # Shift the top bits out and the ciphertext in 452 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) 453 | 454 | encrypted.extend(cipher_segment) 455 | 456 | return _bytes_to_string(encrypted) 457 | 458 | def decrypt(self, ciphertext): 459 | if len(ciphertext) % self._segment_bytes != 0: 460 | raise ValueError('ciphertext block must be a multiple of segment_size') 461 | 462 | ciphertext = _string_to_bytes(ciphertext) 463 | 464 | # Break block into segments 465 | decrypted = [ ] 466 | for i in xrange(0, len(ciphertext), self._segment_bytes): 467 | cipher_segment = ciphertext[i: i + self._segment_bytes] 468 | xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)] 469 | plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ] 470 | 471 | # Shift the top bits out and the ciphertext in 472 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) 473 | 474 | decrypted.extend(plaintext_segment) 475 | 476 | return _bytes_to_string(decrypted) 477 | 478 | 479 | 480 | class AESModeOfOperationOFB(AESStreamModeOfOperation): 481 | '''AES Output Feedback Mode of Operation. 482 | 483 | o A stream-cipher, so input does not need to be padded to blocks, 484 | allowing arbitrary length data. 485 | o A bit twiddled in the cipher text, twiddles the same bit in the 486 | same bit in the plain text, which can be useful for error 487 | correction techniques. 488 | 489 | Also see: 490 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29 491 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4''' 492 | 493 | 494 | name = "Output Feedback (OFB)" 495 | 496 | def __init__(self, key, iv = None): 497 | if iv is None: 498 | self._last_precipherblock = [ 0 ] * 16 499 | elif len(iv) != 16: 500 | raise ValueError('initialization vector must be 16 bytes') 501 | else: 502 | self._last_precipherblock = _string_to_bytes(iv) 503 | 504 | self._remaining_block = [ ] 505 | 506 | AESBlockModeOfOperation.__init__(self, key) 507 | 508 | def encrypt(self, plaintext): 509 | encrypted = [ ] 510 | for p in _string_to_bytes(plaintext): 511 | if len(self._remaining_block) == 0: 512 | self._remaining_block = self._aes.encrypt(self._last_precipherblock) 513 | self._last_precipherblock = [ ] 514 | precipherbyte = self._remaining_block.pop(0) 515 | self._last_precipherblock.append(precipherbyte) 516 | cipherbyte = p ^ precipherbyte 517 | encrypted.append(cipherbyte) 518 | 519 | return _bytes_to_string(encrypted) 520 | 521 | def decrypt(self, ciphertext): 522 | # AES-OFB is symetric 523 | return self.encrypt(ciphertext) 524 | 525 | 526 | 527 | class AESModeOfOperationCTR(AESStreamModeOfOperation): 528 | '''AES Counter Mode of Operation. 529 | 530 | o A stream-cipher, so input does not need to be padded to blocks, 531 | allowing arbitrary length data. 532 | o The counter must be the same size as the key size (ie. len(key)) 533 | o Each block independant of the other, so a corrupt byte will not 534 | damage future blocks. 535 | o Each block has a uniue counter value associated with it, which 536 | contributes to the encrypted value, so no data patterns are 537 | leaked. 538 | o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and 539 | Segmented Integer Counter (SIC 540 | 541 | Security Notes: 542 | o This method (and CBC) ARE recommended. 543 | o Each message block is associated with a counter value which must be 544 | unique for ALL messages with the same key. Otherwise security may be 545 | compromised. 546 | 547 | Also see: 548 | 549 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 550 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5 551 | and Appendix B for managing the initial counter''' 552 | 553 | 554 | name = "Counter (CTR)" 555 | 556 | def __init__(self, key, counter = None): 557 | AESBlockModeOfOperation.__init__(self, key) 558 | 559 | if counter is None: 560 | counter = Counter() 561 | 562 | self._counter = counter 563 | self._remaining_counter = [ ] 564 | 565 | def encrypt(self, plaintext): 566 | while len(self._remaining_counter) < len(plaintext): 567 | self._remaining_counter += self._aes.encrypt(self._counter.value) 568 | self._counter.increment() 569 | 570 | plaintext = _string_to_bytes(plaintext) 571 | 572 | encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ] 573 | self._remaining_counter = self._remaining_counter[len(encrypted):] 574 | 575 | return _bytes_to_string(encrypted) 576 | 577 | def decrypt(self, crypttext): 578 | # AES-CTR is symetric 579 | return self.encrypt(crypttext) 580 | 581 | 582 | # Simple lookup table for each mode 583 | AESModesOfOperation = dict( 584 | ctr = AESModeOfOperationCTR, 585 | cbc = AESModeOfOperationCBC, 586 | cfb = AESModeOfOperationCFB, 587 | ecb = AESModeOfOperationECB, 588 | ofb = AESModeOfOperationOFB, 589 | ) 590 | -------------------------------------------------------------------------------- /pyaes/blockfeeder.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | 24 | from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation 25 | from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable 26 | 27 | 28 | # First we inject three functions to each of the modes of operations 29 | # 30 | # _can_consume(size) 31 | # - Given a size, determine how many bytes could be consumed in 32 | # a single call to either the decrypt or encrypt method 33 | # 34 | # _final_encrypt(data, padding = PADDING_DEFAULT) 35 | # - call and return encrypt on this (last) chunk of data, 36 | # padding as necessary; this will always be at least 16 37 | # bytes unless the total incoming input was less than 16 38 | # bytes 39 | # 40 | # _final_decrypt(data, padding = PADDING_DEFAULT) 41 | # - same as _final_encrypt except for decrypt, for 42 | # stripping off padding 43 | # 44 | 45 | PADDING_NONE = 'none' 46 | PADDING_DEFAULT = 'default' 47 | 48 | # @TODO: Ciphertext stealing and explicit PKCS#7 49 | # PADDING_CIPHERTEXT_STEALING 50 | # PADDING_PKCS7 51 | 52 | # ECB and CBC are block-only ciphers 53 | 54 | def _block_can_consume(self, size): 55 | if size >= 16: return 16 56 | return 0 57 | 58 | # After padding, we may have more than one block 59 | def _block_final_encrypt(self, data, padding = PADDING_DEFAULT): 60 | if padding == PADDING_DEFAULT: 61 | data = append_PKCS7_padding(data) 62 | 63 | elif padding == PADDING_NONE: 64 | if len(data) != 16: 65 | raise Exception('invalid data length for final block') 66 | else: 67 | raise Exception('invalid padding option') 68 | 69 | if len(data) == 32: 70 | return self.encrypt(data[:16]) + self.encrypt(data[16:]) 71 | 72 | return self.encrypt(data) 73 | 74 | 75 | def _block_final_decrypt(self, data, padding = PADDING_DEFAULT): 76 | if padding == PADDING_DEFAULT: 77 | return strip_PKCS7_padding(self.decrypt(data)) 78 | 79 | if padding == PADDING_NONE: 80 | if len(data) != 16: 81 | raise Exception('invalid data length for final block') 82 | return self.decrypt(data) 83 | 84 | raise Exception('invalid padding option') 85 | 86 | AESBlockModeOfOperation._can_consume = _block_can_consume 87 | AESBlockModeOfOperation._final_encrypt = _block_final_encrypt 88 | AESBlockModeOfOperation._final_decrypt = _block_final_decrypt 89 | 90 | 91 | 92 | # CFB is a segment cipher 93 | 94 | def _segment_can_consume(self, size): 95 | return self.segment_bytes * int(size // self.segment_bytes) 96 | 97 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock 98 | def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT): 99 | if padding != PADDING_DEFAULT: 100 | raise Exception('invalid padding option') 101 | 102 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) 103 | padded = data + to_bufferable(faux_padding) 104 | return self.encrypt(padded)[:len(data)] 105 | 106 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock 107 | def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT): 108 | if padding != PADDING_DEFAULT: 109 | raise Exception('invalid padding option') 110 | 111 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) 112 | padded = data + to_bufferable(faux_padding) 113 | return self.decrypt(padded)[:len(data)] 114 | 115 | AESSegmentModeOfOperation._can_consume = _segment_can_consume 116 | AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt 117 | AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt 118 | 119 | 120 | 121 | # OFB and CTR are stream ciphers 122 | 123 | def _stream_can_consume(self, size): 124 | return size 125 | 126 | def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT): 127 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: 128 | raise Exception('invalid padding option') 129 | 130 | return self.encrypt(data) 131 | 132 | def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT): 133 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: 134 | raise Exception('invalid padding option') 135 | 136 | return self.decrypt(data) 137 | 138 | AESStreamModeOfOperation._can_consume = _stream_can_consume 139 | AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt 140 | AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt 141 | 142 | 143 | 144 | class BlockFeeder(object): 145 | '''The super-class for objects to handle chunking a stream of bytes 146 | into the appropriate block size for the underlying mode of operation 147 | and applying (or stripping) padding, as necessary.''' 148 | 149 | def __init__(self, mode, feed, final, padding = PADDING_DEFAULT): 150 | self._mode = mode 151 | self._feed = feed 152 | self._final = final 153 | self._buffer = to_bufferable("") 154 | self._padding = padding 155 | 156 | def feed(self, data = None): 157 | '''Provide bytes to encrypt (or decrypt), returning any bytes 158 | possible from this or any previous calls to feed. 159 | 160 | Call with None or an empty string to flush the mode of 161 | operation and return any final bytes; no further calls to 162 | feed may be made.''' 163 | 164 | if self._buffer is None: 165 | raise ValueError('already finished feeder') 166 | 167 | # Finalize; process the spare bytes we were keeping 168 | if data is None: 169 | result = self._final(self._buffer, self._padding) 170 | self._buffer = None 171 | return result 172 | 173 | self._buffer += to_bufferable(data) 174 | 175 | # We keep 16 bytes around so we can determine padding 176 | result = to_bufferable('') 177 | while len(self._buffer) > 16: 178 | can_consume = self._mode._can_consume(len(self._buffer) - 16) 179 | if can_consume == 0: break 180 | result += self._feed(self._buffer[:can_consume]) 181 | self._buffer = self._buffer[can_consume:] 182 | 183 | return result 184 | 185 | 186 | class Encrypter(BlockFeeder): 187 | 'Accepts bytes of plaintext and returns encrypted ciphertext.' 188 | 189 | def __init__(self, mode, padding = PADDING_DEFAULT): 190 | BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding) 191 | 192 | 193 | class Decrypter(BlockFeeder): 194 | 'Accepts bytes of ciphertext and returns decrypted plaintext.' 195 | 196 | def __init__(self, mode, padding = PADDING_DEFAULT): 197 | BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding) 198 | 199 | 200 | # 8kb blocks 201 | BLOCK_SIZE = (1 << 13) 202 | 203 | def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE): 204 | 'Uses feeder to read and convert from in_stream and write to out_stream.' 205 | 206 | while True: 207 | chunk = in_stream.read(block_size) 208 | if not chunk: 209 | break 210 | converted = feeder.feed(chunk) 211 | out_stream.write(converted) 212 | converted = feeder.feed() 213 | out_stream.write(converted) 214 | 215 | 216 | def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 217 | 'Encrypts a stream of bytes from in_stream to out_stream using mode.' 218 | 219 | encrypter = Encrypter(mode, padding = padding) 220 | _feed_stream(encrypter, in_stream, out_stream, block_size) 221 | 222 | 223 | def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 224 | 'Decrypts a stream of bytes from in_stream to out_stream using mode.' 225 | 226 | decrypter = Decrypter(mode, padding = padding) 227 | _feed_stream(decrypter, in_stream, out_stream, block_size) 228 | -------------------------------------------------------------------------------- /pyaes/util.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # Why to_bufferable? 24 | # Python 3 is very different from Python 2.x when it comes to strings of text 25 | # and strings of bytes; in Python 3, strings of bytes do not exist, instead to 26 | # represent arbitrary binary data, we must use the "bytes" object. This method 27 | # ensures the object behaves as we need it to. 28 | 29 | def to_bufferable(binary): 30 | return binary 31 | 32 | def _get_byte(c): 33 | return ord(c) 34 | 35 | try: 36 | xrange 37 | except: 38 | 39 | def to_bufferable(binary): 40 | if isinstance(binary, bytes): 41 | return binary 42 | return bytes(ord(b) for b in binary) 43 | 44 | def _get_byte(c): 45 | return c 46 | 47 | def append_PKCS7_padding(data): 48 | pad = 16 - (len(data) % 16) 49 | return data + to_bufferable(chr(pad) * pad) 50 | 51 | def strip_PKCS7_padding(data): 52 | if len(data) % 16 != 0: 53 | raise ValueError("invalid length") 54 | 55 | pad = _get_byte(data[-1]) 56 | 57 | if pad > 16: 58 | raise ValueError("invalid padding byte") 59 | 60 | return data[:-pad] 61 | --------------------------------------------------------------------------------