Forking what is left of ZeroNet and hopefully adding an AT Proto Frontend/Proxy
at main 428 lines 17 kB view raw
1import os 2import time 3import io 4import math 5import hashlib 6import re 7import sys 8 9from Config import config 10from Crypt import CryptHash 11from Plugin import PluginManager 12from Debug import Debug 13from util import helper 14 15plugin_dir = os.path.dirname(__file__) 16 17benchmark_key = None 18 19 20@PluginManager.registerTo("UiRequest") 21class UiRequestPlugin(object): 22 @helper.encodeResponse 23 def actionBenchmark(self): 24 global benchmark_key 25 script_nonce = self.getScriptNonce() 26 if not benchmark_key: 27 benchmark_key = CryptHash.random(encoding="base64") 28 self.sendHeader(script_nonce=script_nonce) 29 30 if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local: 31 yield "This function is disabled on this proxy" 32 return 33 34 data = self.render( 35 plugin_dir + "/media/benchmark.html", 36 script_nonce=script_nonce, 37 benchmark_key=benchmark_key, 38 filter=re.sub("[^A-Za-z0-9]", "", self.get.get("filter", "")) 39 ) 40 yield data 41 42 @helper.encodeResponse 43 def actionBenchmarkResult(self): 44 global benchmark_key 45 if self.get.get("benchmark_key", "") != benchmark_key: 46 return self.error403("Invalid benchmark key") 47 48 self.sendHeader(content_type="text/plain", noscript=True) 49 50 if "Multiuser" in PluginManager.plugin_manager.plugin_names and not config.multiuser_local: 51 yield "This function is disabled on this proxy" 52 return 53 54 yield " " * 1024 # Head (required for streaming) 55 56 import main 57 s = time.time() 58 59 for part in main.actions.testBenchmark(filter=self.get.get("filter", "")): 60 yield part 61 62 yield "\n - Total time: %.3fs" % (time.time() - s) 63 64 65@PluginManager.registerTo("Actions") 66class ActionsPlugin: 67 def getMultiplerTitle(self, multipler): 68 if multipler < 0.3: 69 multipler_title = "Sloooow" 70 elif multipler < 0.6: 71 multipler_title = "Ehh" 72 elif multipler < 0.8: 73 multipler_title = "Goodish" 74 elif multipler < 1.2: 75 multipler_title = "OK" 76 elif multipler < 1.7: 77 multipler_title = "Fine" 78 elif multipler < 2.5: 79 multipler_title = "Fast" 80 elif multipler < 3.5: 81 multipler_title = "WOW" 82 else: 83 multipler_title = "Insane!!" 84 return multipler_title 85 86 def formatResult(self, taken, standard): 87 if not standard: 88 return " Done in %.3fs" % taken 89 90 if taken > 0: 91 multipler = standard / taken 92 else: 93 multipler = 99 94 multipler_title = self.getMultiplerTitle(multipler) 95 96 return " Done in %.3fs = %s (%.2fx)" % (taken, multipler_title, multipler) 97 98 def getBenchmarkTests(self, online=False): 99 if hasattr(super(), "getBenchmarkTests"): 100 tests = super().getBenchmarkTests(online) 101 else: 102 tests = [] 103 104 tests.extend([ 105 {"func": self.testHdPrivatekey, "num": 50, "time_standard": 0.57}, 106 {"func": self.testSign, "num": 20, "time_standard": 0.46}, 107 {"func": self.testVerify, "kwargs": {"lib_verify": "sslcrypto_fallback"}, "num": 20, "time_standard": 0.38}, 108 {"func": self.testVerify, "kwargs": {"lib_verify": "sslcrypto"}, "num": 200, "time_standard": 0.30}, 109 {"func": self.testVerify, "kwargs": {"lib_verify": "libsecp256k1"}, "num": 200, "time_standard": 0.10}, 110 111 {"func": self.testPackMsgpack, "num": 100, "time_standard": 0.35}, 112 {"func": self.testUnpackMsgpackStreaming, "kwargs": {"fallback": False}, "num": 100, "time_standard": 0.35}, 113 {"func": self.testUnpackMsgpackStreaming, "kwargs": {"fallback": True}, "num": 10, "time_standard": 0.5}, 114 115 {"func": self.testPackZip, "num": 5, "time_standard": 0.065}, 116 {"func": self.testPackArchive, "kwargs": {"archive_type": "gz"}, "num": 5, "time_standard": 0.08}, 117 {"func": self.testPackArchive, "kwargs": {"archive_type": "bz2"}, "num": 5, "time_standard": 0.68}, 118 {"func": self.testPackArchive, "kwargs": {"archive_type": "xz"}, "num": 5, "time_standard": 0.47}, 119 {"func": self.testUnpackZip, "num": 20, "time_standard": 0.25}, 120 {"func": self.testUnpackArchive, "kwargs": {"archive_type": "gz"}, "num": 20, "time_standard": 0.28}, 121 {"func": self.testUnpackArchive, "kwargs": {"archive_type": "bz2"}, "num": 20, "time_standard": 0.83}, 122 {"func": self.testUnpackArchive, "kwargs": {"archive_type": "xz"}, "num": 20, "time_standard": 0.38}, 123 124 {"func": self.testCryptHash, "kwargs": {"hash_type": "sha256"}, "num": 10, "time_standard": 0.50}, 125 {"func": self.testCryptHash, "kwargs": {"hash_type": "sha512"}, "num": 10, "time_standard": 0.33}, 126 {"func": self.testCryptHashlib, "kwargs": {"hash_type": "sha3_256"}, "num": 10, "time_standard": 0.33}, 127 {"func": self.testCryptHashlib, "kwargs": {"hash_type": "sha3_512"}, "num": 10, "time_standard": 0.65}, 128 129 {"func": self.testRandom, "num": 100, "time_standard": 0.08}, 130 ]) 131 132 if online: 133 tests += [ 134 {"func": self.testHttps, "num": 1, "time_standard": 2.1} 135 ] 136 return tests 137 138 def testBenchmark(self, num_multipler=1, online=False, num_run=None, filter=None): 139 """ 140 Run benchmark on client functions 141 """ 142 tests = self.getBenchmarkTests(online=online) 143 144 if filter: 145 tests = [test for test in tests[:] if filter.lower() in test["func"].__name__.lower()] 146 147 yield "\n" 148 res = {} 149 res_time_taken = {} 150 multiplers = [] 151 for test in tests: 152 s = time.time() 153 if num_run: 154 num_run_test = num_run 155 else: 156 num_run_test = math.ceil(test["num"] * num_multipler) 157 func = test["func"] 158 func_name = func.__name__ 159 kwargs = test.get("kwargs", {}) 160 key = "%s %s" % (func_name, kwargs) 161 if kwargs: 162 yield "* Running %s (%s) x %s " % (func_name, kwargs, num_run_test) 163 else: 164 yield "* Running %s x %s " % (func_name, num_run_test) 165 i = 0 166 try: 167 for progress in func(num_run_test, **kwargs): 168 i += 1 169 if num_run_test > 10: 170 should_print = i % (num_run_test / 10) == 0 or progress != "." 171 else: 172 should_print = True 173 174 if should_print: 175 if num_run_test == 1 and progress == ".": 176 progress = "..." 177 yield progress 178 time_taken = time.time() - s 179 if num_run: 180 time_standard = 0 181 else: 182 time_standard = test["time_standard"] * num_multipler 183 yield self.formatResult(time_taken, time_standard) 184 yield "\n" 185 res[key] = "ok" 186 res_time_taken[key] = time_taken 187 multiplers.append(time_standard / max(time_taken, 0.001)) 188 except Exception as err: 189 res[key] = err 190 yield "Failed!\n! Error: %s\n\n" % Debug.formatException(err) 191 192 yield "\n== Result ==\n" 193 194 # Check verification speed 195 if "testVerify {'lib_verify': 'sslcrypto'}" in res_time_taken: 196 speed_order = ["sslcrypto_fallback", "sslcrypto", "libsecp256k1"] 197 time_taken = {} 198 for lib_verify in speed_order: 199 time_taken[lib_verify] = res_time_taken["testVerify {'lib_verify': '%s'}" % lib_verify] 200 201 time_taken["sslcrypto_fallback"] *= 10 # fallback benchmark only run 20 times instead of 200 202 speedup_sslcrypto = time_taken["sslcrypto_fallback"] / time_taken["sslcrypto"] 203 speedup_libsecp256k1 = time_taken["sslcrypto_fallback"] / time_taken["libsecp256k1"] 204 205 yield "\n* Verification speedup:\n" 206 yield " - OpenSSL: %.1fx (reference: 7.0x)\n" % speedup_sslcrypto 207 yield " - libsecp256k1: %.1fx (reference: 23.8x)\n" % speedup_libsecp256k1 208 209 if speedup_sslcrypto < 2: 210 res["Verification speed"] = "error: OpenSSL speedup low: %.1fx" % speedup_sslcrypto 211 212 if speedup_libsecp256k1 < speedup_sslcrypto: 213 res["Verification speed"] = "error: libsecp256k1 speedup low: %.1fx" % speedup_libsecp256k1 214 215 if not res: 216 yield "! No tests found" 217 if config.action == "test": 218 sys.exit(1) 219 else: 220 num_failed = len([res_key for res_key, res_val in res.items() if res_val != "ok"]) 221 num_success = len([res_key for res_key, res_val in res.items() if res_val == "ok"]) 222 yield "\n* Tests:\n" 223 yield " - Total: %s tests\n" % len(res) 224 yield " - Success: %s tests\n" % num_success 225 yield " - Failed: %s tests\n" % num_failed 226 if any(multiplers): 227 multipler_avg = sum(multiplers) / len(multiplers) 228 multipler_title = self.getMultiplerTitle(multipler_avg) 229 yield " - Average speed factor: %.2fx (%s)\n" % (multipler_avg, multipler_title) 230 231 # Display errors 232 for res_key, res_val in res.items(): 233 if res_val != "ok": 234 yield " ! %s %s\n" % (res_key, res_val) 235 236 if num_failed != 0 and config.action == "test": 237 sys.exit(1) 238 239 def testHttps(self, num_run=1): 240 """ 241 Test https connection with valid and invalid certs 242 """ 243 import urllib.request 244 import urllib.error 245 246 body = urllib.request.urlopen("https://google.com").read() 247 assert len(body) > 100 248 yield "." 249 250 badssl_urls = [ 251 "https://expired.badssl.com/", 252 "https://wrong.host.badssl.com/", 253 "https://self-signed.badssl.com/", 254 "https://untrusted-root.badssl.com/" 255 ] 256 for badssl_url in badssl_urls: 257 try: 258 body = urllib.request.urlopen(badssl_url).read() 259 https_err = None 260 except urllib.error.URLError as err: 261 https_err = err 262 assert https_err 263 yield "." 264 265 def testCryptHash(self, num_run=1, hash_type="sha256"): 266 """ 267 Test hashing functions 268 """ 269 yield "(5MB) " 270 271 from Crypt import CryptHash 272 273 hash_types = { 274 "sha256": {"func": CryptHash.sha256sum, "hash_valid": "8cd629d9d6aff6590da8b80782a5046d2673d5917b99d5603c3dcb4005c45ffa"}, 275 "sha512": {"func": CryptHash.sha512sum, "hash_valid": "9ca7e855d430964d5b55b114e95c6bbb114a6d478f6485df93044d87b108904d"} 276 } 277 hash_func = hash_types[hash_type]["func"] 278 hash_valid = hash_types[hash_type]["hash_valid"] 279 280 data = io.BytesIO(b"Hello" * 1024 * 1024) # 5MB 281 for i in range(num_run): 282 data.seek(0) 283 hash = hash_func(data) 284 yield "." 285 assert hash == hash_valid, "%s != %s" % (hash, hash_valid) 286 287 def testCryptHashlib(self, num_run=1, hash_type="sha3_256"): 288 """ 289 Test SHA3 hashing functions 290 """ 291 yield "x 5MB " 292 293 hash_types = { 294 "sha3_256": {"func": hashlib.sha3_256, "hash_valid": "c8aeb3ef9fe5d6404871c0d2a4410a4d4e23268e06735648c9596f436c495f7e"}, 295 "sha3_512": {"func": hashlib.sha3_512, "hash_valid": "b75dba9472d8af3cc945ce49073f3f8214d7ac12086c0453fb08944823dee1ae83b3ffbc87a53a57cc454521d6a26fe73ff0f3be38dddf3f7de5d7692ebc7f95"}, 296 } 297 298 hash_func = hash_types[hash_type]["func"] 299 hash_valid = hash_types[hash_type]["hash_valid"] 300 301 data = io.BytesIO(b"Hello" * 1024 * 1024) # 5MB 302 for i in range(num_run): 303 data.seek(0) 304 h = hash_func() 305 while 1: 306 buff = data.read(1024 * 64) 307 if not buff: 308 break 309 h.update(buff) 310 hash = h.hexdigest() 311 yield "." 312 assert hash == hash_valid, "%s != %s" % (hash, hash_valid) 313 314 def testRandom(self, num_run=1): 315 """ 316 Test generating random data 317 """ 318 yield "x 1000 x 256 bytes " 319 for i in range(num_run): 320 data_last = None 321 for y in range(1000): 322 data = os.urandom(256) 323 assert data != data_last 324 assert len(data) == 256 325 data_last = data 326 yield "." 327 328 def testHdPrivatekey(self, num_run=2): 329 """ 330 Test generating deterministic private keys from a master seed 331 """ 332 from Crypt import CryptBitcoin 333 seed = "e180efa477c63b0f2757eac7b1cce781877177fe0966be62754ffd4c8592ce38" 334 privatekeys = [] 335 for i in range(num_run): 336 privatekeys.append(CryptBitcoin.hdPrivatekey(seed, i * 10)) 337 yield "." 338 valid = "5JSbeF5PevdrsYjunqpg7kAGbnCVYa1T4APSL3QRu8EoAmXRc7Y" 339 assert privatekeys[0] == valid, "%s != %s" % (privatekeys[0], valid) 340 if len(privatekeys) > 1: 341 assert privatekeys[0] != privatekeys[-1] 342 343 def testSign(self, num_run=1): 344 """ 345 Test signing data using a private key 346 """ 347 from Crypt import CryptBitcoin 348 data = "Hello" * 1024 349 privatekey = "5JsunC55XGVqFQj5kPGK4MWgTL26jKbnPhjnmchSNPo75XXCwtk" 350 for i in range(num_run): 351 yield "." 352 sign = CryptBitcoin.sign(data, privatekey) 353 valid = "G1GXaDauZ8vX/N9Jn+MRiGm9h+I94zUhDnNYFaqMGuOiBHB+kp4cRPZOL7l1yqK5BHa6J+W97bMjvTXtxzljp6w=" 354 assert sign == valid, "%s != %s" % (sign, valid) 355 356 def testVerify(self, num_run=1, lib_verify="sslcrypto"): 357 """ 358 Test verification of generated signatures 359 """ 360 from Crypt import CryptBitcoin 361 CryptBitcoin.loadLib(lib_verify, silent=True) 362 363 364 data = "Hello" * 1024 365 privatekey = "5JsunC55XGVqFQj5kPGK4MWgTL26jKbnPhjnmchSNPo75XXCwtk" 366 address = CryptBitcoin.privatekeyToAddress(privatekey) 367 sign = "G1GXaDauZ8vX/N9Jn+MRiGm9h+I94zUhDnNYFaqMGuOiBHB+kp4cRPZOL7l1yqK5BHa6J+W97bMjvTXtxzljp6w=" 368 369 for i in range(num_run): 370 ok = CryptBitcoin.verify(data, address, sign, lib_verify=lib_verify) 371 yield "." 372 assert ok, "does not verify from %s" % address 373 374 if lib_verify == "sslcrypto": 375 yield("(%s)" % CryptBitcoin.sslcrypto.ecc.get_backend()) 376 377 def testPortCheckers(self): 378 """ 379 Test all active open port checker 380 """ 381 from Peer import PeerPortchecker 382 for ip_type, func_names in PeerPortchecker.PeerPortchecker.checker_functions.items(): 383 yield "\n- %s:" % ip_type 384 for func_name in func_names: 385 yield "\n - Tracker %s: " % func_name 386 try: 387 for res in self.testPortChecker(func_name): 388 yield res 389 except Exception as err: 390 yield Debug.formatException(err) 391 392 def testPortChecker(self, func_name): 393 """ 394 Test single open port checker 395 """ 396 from Peer import PeerPortchecker 397 peer_portchecker = PeerPortchecker.PeerPortchecker(None) 398 announce_func = getattr(peer_portchecker, func_name) 399 res = announce_func(3894) 400 yield res 401 402 def testAll(self): 403 """ 404 Run all tests to check system compatibility with ZeroNet functions 405 """ 406 for progress in self.testBenchmark(online=not config.offline, num_run=1): 407 yield progress 408 409 410@PluginManager.registerTo("ConfigPlugin") 411class ConfigPlugin(object): 412 def createArguments(self): 413 back = super(ConfigPlugin, self).createArguments() 414 if self.getCmdlineValue("test") == "benchmark": 415 self.test_parser.add_argument( 416 '--num_multipler', help='Benchmark run time multipler', 417 default=1.0, type=float, metavar='num' 418 ) 419 self.test_parser.add_argument( 420 '--filter', help='Filter running benchmark', 421 default=None, metavar='test name' 422 ) 423 elif self.getCmdlineValue("test") == "portChecker": 424 self.test_parser.add_argument( 425 '--func_name', help='Name of open port checker function', 426 default=None, metavar='func_name' 427 ) 428 return back