# Written by John Hoffman # based on code by Uoti Urpala # see LICENSE.txt for license information from __future__ import generators # for python 2.2 from random import randrange,randint,seed try: from os import urandom except: seed() urandom = lambda x: ''.join([chr(randint(0,255)) for i in xrange(x)]) from sha import sha try: True except: True = 1 False = 0 try: from Crypto.Cipher import ARC4 CRYPTO_OK = True except: CRYPTO_OK = False KEY_LENGTH = 160 DH_PRIME = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563 PAD_MAX = 200 # less than protocol maximum, and later assumed to be < 256 DH_BYTES = 96 def bytetonum(x): return long(x.encode('hex'), 16) def numtobyte(x): x = hex(x).lstrip('0x').rstrip('Ll') x = '0'*(192 - len(x)) + x return x.decode('hex') class Crypto: def __init__(self, initiator, disable_crypto = False): self.initiator = initiator self.disable_crypto = disable_crypto if not disable_crypto and not CRYPTO_OK: raise NotImplementedError, "attempt to run encryption w/ none installed" self.privkey = bytetonum(urandom(KEY_LENGTH/8)) self.pubkey = numtobyte(pow(2, self.privkey, DH_PRIME)) self.keylength = DH_BYTES self._VC_pattern = None def received_key(self, k): self.S = numtobyte(pow(bytetonum(k), self.privkey, DH_PRIME)) self.block3a = sha('req1'+self.S).digest() self.block3bkey = sha('req3'+self.S).digest() self.block3b = None def _gen_block3b(self, SKEY): a = sha('req2'+SKEY).digest() return ''.join([ chr(ord(a[i])^ord(self.block3bkey[i])) for i in xrange(20) ]) def test_skey(self, s, SKEY): block3b = self._gen_block3b(SKEY) if block3b != s: return False self.block3b = block3b if not self.disable_crypto: self.set_skey(SKEY) return True def set_skey(self, SKEY): if not self.block3b: self.block3b = self._gen_block3b(SKEY) crypta = ARC4.new(sha('keyA'+self.S+SKEY).digest()) cryptb = ARC4.new(sha('keyB'+self.S+SKEY).digest()) if self.initiator: self.encrypt = crypta.encrypt self.decrypt = cryptb.decrypt else: self.encrypt = cryptb.encrypt self.decrypt = crypta.decrypt self.encrypt('x'*1024) # discard first 1024 bytes self.decrypt('x'*1024) def VC_pattern(self): if not self._VC_pattern: self._VC_pattern = self.decrypt('\x00'*8) return self._VC_pattern def read(self, s): self._read(self.decrypt(s)) def write(self, s): self._write(self.encrypt(s)) def setrawaccess(self, _read, _write): self._read = _read self._write = _write def padding(self): return urandom(randrange(PAD_MAX-16)+16)