Exemple #1
0
 def getKey(self):
     with newkey_lock[self.serverid]:
         if self.serverid not in keys:
             for _ in range(2):
                 logger.debug('hxsocks getKey')
                 host, port, usn, psw = (self.hxsServer.hostname, self.hxsServer.port, self.hxsServer.username, self.hxsServer.password)
                 if self._sock is None:
                     logger.debug('hxsocks connect')
                     from connection import create_connection
                     self._sock = create_connection((host, port), self.timeout, parentproxy=self.parentproxy, tunnel=True)
                     self.pskcipher = encrypt.Encryptor(self.PSK, self.method)
                 acipher = ECC(self.pskcipher.key_len)
                 pubk = acipher.get_pub_key()
                 logger.debug('hxsocks send key exchange request')
                 ts = struct.pack('>I', int(time.time()))
                 padding_len = random.randint(64, 255)
                 data = chr(10) + ts + chr(len(pubk)) + pubk + hmac.new(psw.encode(), ts + pubk + usn.encode(), hashlib.sha256).digest()\
                     + chr(padding_len) + b'\x00' * padding_len
                 self._sock.sendall(self.pskcipher.encrypt(data))
                 fp = self._sock.makefile('rb', 0)
                 resp_len = 1 if self.pskcipher.decipher else self.pskcipher.iv_len + 1
                 resp = ord(self.pskcipher.decrypt(fp.read(resp_len)))
                 if resp == 0:
                     logger.debug('hxsocks read key exchange respond')
                     pklen = ord(self.pskcipher.decrypt(fp.read(1)))
                     server_key = self.pskcipher.decrypt(fp.read(pklen))
                     auth = self.pskcipher.decrypt(fp.read(32))
                     pklen = ord(self.pskcipher.decrypt(fp.read(1)))
                     server_cert = self.pskcipher.decrypt(fp.read(pklen))
                     sig_len = ord(self.pskcipher.decrypt(fp.read(1)))
                     signature = self.pskcipher.decrypt(fp.read(sig_len))
                     pad_len = ord(self.pskcipher.decrypt(fp.read(1)))
                     self.pskcipher.decrypt(fp.read(pad_len))
                     # TODO: ask user if a certificate should be accepted or not.
                     if host not in known_hosts:
                         logger.info('hxs: server %s new cert %s saved.' % (host, hashlib.sha256(server_cert).hexdigest()[:8]))
                         with open('./.hxs_known_hosts/' + host + '.cert', 'wb') as f:
                             f.write(server_cert)
                             known_hosts[host] = server_cert
                     elif known_hosts[host] != server_cert:
                         logger.error('hxs: server %s certificate mismatch! PLEASE CHECK!' % host)
                         raise OSError(0, 'hxs: bad certificate')
                     if auth == hmac.new(psw.encode(), pubk + server_key + usn.encode(), hashlib.sha256).digest():
                         if ECC.verify_with_pub_key(server_cert, auth, signature, self.hash_algo):
                             shared_secret = acipher.get_dh_key(server_key)
                             keys[self.serverid] = (hashlib.md5(pubk).digest(), shared_secret)
                             self.cipher = encrypt.AEncryptor(keys[self.serverid][1], self.method, SALT, CTX, 0)
                             logger.debug('hxs key exchange success')
                             return
                         else:
                             logger.error('hxs getKey Error: server auth failed, bad signature')
                     else:
                         logger.error('hxs getKey Error: server auth failed, bad username or password')
                 else:
                     fp.read(ord(self.pskcipher.decrypt(fp.read(1))))
                     logger.error('hxs getKey Error. bad password or timestamp.')
             else:
                 raise IOError(0, 'hxs getKey Error')
         else:
             self.cipher = encrypt.AEncryptor(keys[self.serverid][1], self.method, SALT, CTX, 0)
Exemple #2
0
	def Authenticate(self, PubKey, PubKeySignature):
		ecc = ECC(pubkey_data=PubKey)
		if not ecc.verifyAddress(PubKeySignature, PubKey):
			raise ValueError('Failed to verify public key data.')
		self.stream.remote_ecc = ecc
		self.auth = True
		self.stream.node.add_stream(self.stream)
		return True
Exemple #3
0
 def create_key(cls, user, client_pkey, klen):
     if cls.notvalid(user, client_pkey):
         return 0, 0
     if len(cls.userpkeys[user]) > 3:
         cls.del_key(cls.userpkeys[user][0])
     dh = ECC(klen)
     shared_secret = dh.get_dh_key(client_pkey)
     client_pkey = hashlib.md5(client_pkey).digest()
     cls.userpkeys[user].append(client_pkey)
     cls.pkeyuser[client_pkey] = user
     cls.pkeykey[client_pkey] = shared_secret
     cls.pkeytime[client_pkey] = time.time()
     return dh.get_pub_key(), USER_PASS[user]
Exemple #4
0
def A2MXAccessStore(node, data):
	b58node = ECC.b58(node)
	if mongoclient == None or b58node not in mongoclient.database_names():
		raise A2MXAccessException('Unknown node {}'.format(b58node))
	# FIXME check if data already in DB
	_id = mongoclient[b58node]['inbox'].insert({ 'timestamp': datetime.datetime.now(datetime.timezone.utf), 'data': data })

	rid = int(0).to_bytes(4)
	data = rid + BSON.encode({ '_id': _id })
	clients = connected_clients.online(node)
	for client in clients:
		client.sendfun(data)
Exemple #5
0
	def __init__(self, selectloop):
		self.selectloop = selectloop
		self.pathlist = a2mxpath.PathList()

		self.streams = []
		self.ecc = ECC(keyfile=config['keyfile'])
		self.connected_nodes = { self.ecc.pubkeyHash(): self }

		mypub = self.ecc.pubkeyHashBase58()
		if sys.stdout.isatty():
			cwd = os.getcwd().rsplit('/', 1)[1]
			sys.stdout.write("\x1b]2;{}: {}\x07".format(cwd, mypub))
		print("I am", mypub)

		self.selectloop.tadd(90, self.find_new_peers)
Exemple #6
0
	def process_bson(self, bs):
		if self.ecc == None:
			self.ecc = ECC(pubkey_data=bs['access'])
			if self.ecc.pubkeyHash() != self.node.ecc.pubkeyHash():
				if mongoclient == None or self.ecc.pubkeyHashBase58() not in mongoclient.database_names():
					return { 'error': 'Unknown Node {}'.format(self.ecc.pubkeyHashBase58()) }
				self.db = mongoclient[self.ecc.pubkeyHashBase58()]
				print("access request to", self.ecc.pubkeyHashBase58())
			else:
				print("access to me")
			self.auth = now()
			return { 'auth': self.auth, 'pubkey': self.node.ecc.pubkeyAddress() }
		if isinstance(self.auth, datetime.datetime):
			sigdata = BSON.encode({ 'auth': self.auth })
			verify = self.ecc.verifyAddress(bs['sig'], sigdata)
			lsig = self.node.ecc.signAddress(sigdata)
			if not verify:
				return { 'error': 'Not authenticated' }

			self.auth = True
			connected_clients.add(self)

			return { 'sig': lsig }

		if self.auth != True:
			return { 'error': 'Not authenticated' }

		if len(bs) != 1:
			raise A2MXAccessException('Only one command at a time supported')
		for k, v in bs.items():
			f = getattr(self, k, None)
			if getattr(f, 'A2MXAccessRequest__marker__', False) != True:
				raise A2MXAccessException('Invalid request {}'.format(k))
			if isinstance(v, (dict, OrderedDict)):
				return f(**v)
			elif v == None:
				return f()
			raise A2MXAccessException('Invalid argument '.format(v))
Exemple #7
0
def main():
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s %(levelname)-8s %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S',
                        filemode='a+')

    hello = 'hxsocks-server %s' % __version__
    if gevent:
        hello += ' with gevent %s' % gevent.__version__
    print(hello)
    print('by v3aqb')
    global SERVER_CERT
    try:
        SERVER_CERT = ECC(from_file=os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'cert.pem'))
    except:
        logging.warning('server cert not found, creating...')
        SERVER_CERT = ECC(key_len=32)
        SERVER_CERT.save(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         'cert.pem'))

    servers = ['hxs://0.0.0.0:9000']
    forward = []
    if os.path.exists(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         'config.json')):
        global USER_PASS
        d = json.loads(
            open(
                os.path.join(os.path.dirname(os.path.abspath(__file__)),
                             'config.json')).read())
        USER_PASS = d['users']
        servers = d['servers']
        forward = d.get('forward', [])
    for s in servers:
        logging.info('starting server: %s' % s)
        ssserver = HXSocksServer(s, forward, HXSocksHandler)
        threading.Thread(target=ssserver.serve_forever).start()
Exemple #8
0
    def getKey(self):
        with newkey_lock[self.serverid]:
            if self.serverid not in keys:
                for _ in range(2):
                    logger.debug('hxsocks getKey')
                    host, port, usn, psw = (self.hxsServer.hostname,
                                            self.hxsServer.port,
                                            self.hxsServer.username,
                                            self.hxsServer.password)
                    if self._sock is None:
                        logger.debug('hxsocks connect')
                        from connection import create_connection
                        self._sock = create_connection(
                            (host, port),
                            self.timeout,
                            parentproxy=self.parentproxy,
                            tunnel=True)
                        self.pskcipher = encrypt.Encryptor(
                            self.PSK, self.method)
                        self._rfile = self._sock.makefile('rb')
                        self._header_sent = False
                        self._header_received = False
                    acipher = ECC(self.pskcipher.key_len)
                    pubk = acipher.get_pub_key()
                    logger.debug('hxsocks send key exchange request')
                    ts = struct.pack('>I', int(time.time()))
                    padding_len = random.randint(64, 255)
                    data = b''.join([
                        ts,
                        chr(len(pubk)).encode('latin1'), pubk,
                        hmac.new(psw.encode(), ts + pubk + usn.encode(),
                                 hashlib.sha256).digest(),
                        b'\x00' * padding_len
                    ])
                    data = chr(10).encode() + struct.pack('>H',
                                                          len(data)) + data
                    self._sock_sendall(self.pskcipher.encrypt(data))
                    resp_len = 2 if self.pskcipher.decipher else self.pskcipher.iv_len + 2
                    resp_len = self.pskcipher.decrypt(
                        self._rfile_read(resp_len))
                    resp_len = struct.unpack('>H', resp_len)[0]
                    data = self.pskcipher.decrypt(self._rfile_read(resp_len))

                    data = io.BytesIO(data)

                    resp_code = byte2int(data.read(1))
                    if resp_code == 0:
                        logger.debug('hxsocks read key exchange respond')
                        pklen = byte2int(data.read(1))
                        scertlen = byte2int(data.read(1))
                        siglen = byte2int(data.read(1))

                        server_key = data.read(pklen)
                        auth = data.read(32)
                        server_cert = data.read(scertlen)
                        signature = data.read(siglen)

                        # TODO: ask user if a certificate should be accepted or not.
                        if host not in known_hosts:
                            logger.info(
                                'hxs: server %s new cert %s saved.' %
                                (host,
                                 hashlib.sha256(server_cert).hexdigest()[:8]))
                            with open('./.hxs_known_hosts/' + host + '.cert',
                                      'wb') as f:
                                f.write(server_cert)
                                known_hosts[host] = server_cert
                        elif known_hosts[host] != server_cert:
                            logger.error(
                                'hxs: server %s certificate mismatch! PLEASE CHECK!'
                                % host)
                            raise OSError(0, 'hxs: bad certificate')
                        if auth == hmac.new(psw.encode(),
                                            pubk + server_key + usn.encode(),
                                            hashlib.sha256).digest():
                            if ECC.verify_with_pub_key(server_cert, auth,
                                                       signature,
                                                       self.hash_algo):
                                shared_secret = acipher.get_dh_key(server_key)
                                keys[self.serverid] = (
                                    hashlib.md5(pubk).digest(), shared_secret)
                                self.cipher = encrypt.AEncryptor(
                                    keys[self.serverid][1], self.method, SALT,
                                    CTX, 0, MAC_LEN)
                                logger.debug('hxs key exchange success')
                                return
                            else:
                                logger.error(
                                    'hxs getKey Error: server auth failed, bad signature'
                                )
                        else:
                            logger.error(
                                'hxs getKey Error: server auth failed, bad username or password'
                            )
                    else:
                        logger.error(
                            'hxs getKey Error. bad password or timestamp.')
                else:
                    raise IOError(0, 'hxs getKey Error')
            else:
                self.cipher = encrypt.AEncryptor(keys[self.serverid][1],
                                                 self.method, SALT, CTX, 0,
                                                 MAC_LEN)
Exemple #9
0
class A2MXAccess():
	def __init__(self, node, sendfun):
		self.node = node
		self.sendfun = sendfun
		self.ecc = None
		self.auth = False

	def disconnected(self):
		if self.auth:
			connected_clients.remove(self)
		print("A2MXAccess disconnect", self.ecc.pubkeyHashBase58() if self.ecc else "unknown", "authenticated" if self.auth == True else "not authenticated")

	def process(self, data):
		rid = data[:4]
		bs = BSON.decode(bytes(data[4:]), tz_aware=True)
		try:
			value = self.process_bson(bs)
		except Exception as e:
			import traceback
			traceback.print_exc()
			value = None
			error = 'Exception occured: {} {}'.format(str(type(e)), str(e))
		else:
			error = None
		response = {}
		if isinstance(value, (dict, OrderedDict)):
			response = value
		else:
			if value != None:
				response = { 'data': value }
			if error != None:
				response = { 'error': error }

		if len(response):
			bv = BSON.encode(response)
			self.sendfun(rid + bv)

	def process_bson(self, bs):
		if self.ecc == None:
			self.ecc = ECC(pubkey_data=bs['access'])
			if self.ecc.pubkeyHash() != self.node.ecc.pubkeyHash():
				if mongoclient == None or self.ecc.pubkeyHashBase58() not in mongoclient.database_names():
					return { 'error': 'Unknown Node {}'.format(self.ecc.pubkeyHashBase58()) }
				self.db = mongoclient[self.ecc.pubkeyHashBase58()]
				print("access request to", self.ecc.pubkeyHashBase58())
			else:
				print("access to me")
			self.auth = now()
			return { 'auth': self.auth, 'pubkey': self.node.ecc.pubkeyAddress() }
		if isinstance(self.auth, datetime.datetime):
			sigdata = BSON.encode({ 'auth': self.auth })
			verify = self.ecc.verifyAddress(bs['sig'], sigdata)
			lsig = self.node.ecc.signAddress(sigdata)
			if not verify:
				return { 'error': 'Not authenticated' }

			self.auth = True
			connected_clients.add(self)

			return { 'sig': lsig }

		if self.auth != True:
			return { 'error': 'Not authenticated' }

		if len(bs) != 1:
			raise A2MXAccessException('Only one command at a time supported')
		for k, v in bs.items():
			f = getattr(self, k, None)
			if getattr(f, 'A2MXAccessRequest__marker__', False) != True:
				raise A2MXAccessException('Invalid request {}'.format(k))
			if isinstance(v, (dict, OrderedDict)):
				return f(**v)
			elif v == None:
				return f()
			raise A2MXAccessException('Invalid argument '.format(v))

	@A2MXAccessRequest
	def getpath(self):
		p = A2MXPath(self.node.ecc, self.ecc, no_URI=True)
		return p.data

	@A2MXAccessRequest
	def setpath(self, **kwargs):
		p = A2MXPath(**kwargs)
		assert p.isComplete

		self.db['path'].remove()
		self.db['path'].insert(p.data)
		self.node.new_path(p)
		return True

	@A2MXAccessRequest
	def paths(self):
		paths = [ p.data for p in self.node.paths ]
		return paths

	@A2MXAccessRequest
	def find(self, query, rep):
		return [ x for x in self.db['inbox'].find(query, rep) ]

	@A2MXAccessRequest
	def save(self, doc):
		return self.db['inbox'].save(doc)

	@A2MXAccessRequest
	def find_routes(self, src, dst, min_hops, max_hops, max_count):
		if not src or src == b'\x00':
			src = self.node.ecc.pubkeyHash()
		routes = self.node.find_routes_from(src, dst, max_hops)
		send = []
		for route in routes:
			if len(route) < min_hops:
				continue
			send.append(route.routes)
			if len(send) >= max_count:
				break
		return send

	@A2MXAccessRequest
	def sendto(self, node, data):
		return self.node.sendto(node, data)
Exemple #10
0
    def getKey(self):
        logger.debug('hxsocks2 getKey')
        host, port, usn, psw = (self.hxsServer.hostname, self.hxsServer.port, self.hxsServer.username, self.hxsServer.password)
        logger.debug('hxsocks2 connect to server')
        from connection import create_connection
        self._sock = create_connection((host, port), self.timeout, parentproxy=self.parentproxy, tunnel=True)

        self._rfile = self._sock.makefile('rb')

        acipher = ECC(self.__pskcipher._key_len)
        pubk = acipher.get_pub_key()
        logger.debug('hxsocks2 send key exchange request')
        ts = int(time.time()) // 30
        ts = struct.pack('>I', ts)
        padding_len = random.randint(64, 255)
        data = b''.join([chr(len(pubk)).encode('latin1'),
                         pubk,
                         hmac.new(psw.encode(), ts + pubk + usn.encode(), hashlib.sha256).digest(),
                         b'\x00' * padding_len])
        data = chr(20).encode() + struct.pack('>H', len(data)) + data

        ct = self.__pskcipher.encrypt(data)
        self._sock.sendall(ct)

        self.__pskcipher.decrypt(self._rfile.read(self.__pskcipher._iv_len))

        if encrypt.is_aead(self.method):
            ct_len = self.__pskcipher.decrypt(self._rfile.read(18))
            ct_len, = struct.unpack('!H', ct_len)
            ct = self.__pskcipher.decrypt(self._rfile.read(ct_len + 16))
            data = ct[2:]
        else:
            resp_len = self.__pskcipher.decrypt(self._rfile.read(2))
            resp_len, = struct.unpack('>H', resp_len)
            data = self.__pskcipher.decrypt(self._rfile.read(resp_len))

        data = io.BytesIO(data)

        resp_code = byte2int(data.read(1))
        if resp_code == 0:
            logger.debug('hxsocks read key exchange respond')
            pklen = byte2int(data.read(1))
            scertlen = byte2int(data.read(1))
            siglen = byte2int(data.read(1))

            server_key = data.read(pklen)
            auth = data.read(32)
            server_cert = data.read(scertlen)
            signature = data.read(siglen)

            # TODO: ask user if a certificate should be accepted or not.
            server_id = '%s_%d' % (host, port)
            if server_id not in known_hosts:
                logger.info('hxs: server %s new cert %s saved.' % (server_id, hashlib.sha256(server_cert).hexdigest()[:8]))
                with open('./.hxs_known_hosts/' + server_id + '.cert', 'wb') as f:
                    f.write(server_cert)
                    known_hosts[server_id] = server_cert
            elif known_hosts[server_id] != server_cert:
                logger.error('hxs: server %s certificate mismatch! PLEASE CHECK!' % server_id)
                raise OSError(0, 'hxs: bad certificate')

            if auth == hmac.new(psw.encode(), pubk + server_key + usn.encode(), hashlib.sha256).digest():
                if ECC.verify_with_pub_key(server_cert, auth, signature, self.hash_algo):
                    shared_secret = acipher.get_dh_key(server_key)
                    logger.debug('hxs key exchange success')
                    self.__cipher = AEncryptor(shared_secret, self.method, CTX)
                    return
                else:
                    logger.error('hxs getKey Error: server auth failed, bad signature')
            else:
                logger.error('hxs getKey Error: server auth failed, bad username or password')
        else:
            logger.error('hxs getKey Error. bad password or timestamp.')
        raise OSError(0, 'hxs getKey Error')
Exemple #11
0
if len(sys.argv) == 1:
	host = 'localhost'
	port = config['bind'][0][1]
else:
	hostport = sys.argv[1].split(':')
	if len(hostport) == 1:
		host = hostport[0]
		port = 0xA22
	elif len(hostport) == 2:
		host = hostport[0]
		port = int(hostport[1])
	else:
		raise ValueError('invalid arguments')

ecc = ECC(pkcs8_der_keyfile_address=config['address.pkcs8.der'], pkcs8_der_keyfile_sign=config['sign.pkcs8.der'], pkcs8_der_keyfile_encrypt=config['encrypt.pkcs8.der'])
print("I am", ecc.pubkeyHashBase58())

def SSL(sock):
	context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
	context.verify_mode = ssl.CERT_NONE
	context.set_ecdh_curve('secp521r1')
	context.options = ssl.OP_SINGLE_DH_USE | ssl.OP_SINGLE_ECDH_USE
	context.set_ciphers('ECDHE-ECDSA-AES256-SHA')
	return context.wrap_socket(sock, do_handshake_on_connect=True)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.connect((host, port))
sock = SSL(sock)
Exemple #12
0
	def __init__(self, A=None, B=None, T=None, UA=None, UB=None, M=None, PB=None, PF=None, PD=None, P=None, SA=None, SB=None, D=None, DS=None):
		# A = node A public key data (address, sign and encrypt compressed public keys)
		# B = node B public key data
		# T = timestamp
		# UA = AX URI node A
		# UB = AX URI node B
		# M = MaxSize
		# PB = POW broadcast
		# PF = POW forward
		# PD = POW direct
		# P = path proof of work
		# SA = node A signature (over A, B, T and UA if present)
		# SB = node B signature (over A, B, T and UB if present)
		# D = deleted timestamp
		# DS = deleted signature (over A, B, T, SA, SB and D)
		# A must always be the smaller binary value

		if not isinstance(A, ECC):
			self.__a = ECC(pubkey_data=A)
		else:
			self.__a = A
			if not SA and self.__a.hasPrivkey() and not PB < 0:
				UA = config['publish_axuri']

		if not isinstance(B, ECC):
			self.__b = ECC(pubkey_data=B)
		else:
			self.__b = B
			if not SB and self.__b.hasPrivkey() and not PB < 0:
				UB = config['publish_axuri']

		def testURI(uri):
			if uri == None:
				return
			if len(uri) > 32:
				raise ValueError('URI too long')
			try:
				host, port = uri.split(':')
			except ValueError:
				raise ValueError('Invalid URI')
			try:
				int(port)
			except ValueError:
				raise ValueError('Invalid URI')
			validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789'
			if not all(c in validChars for c in host):
				raise ValueError('Invalid chars in URI')
		testURI(UA)
		testURI(UB)

		self.__ua = UA
		self.__ub = UB

		if self.__a.pubkeyData() > self.__b.pubkeyData():
			self.__a, self.__b = self.__b, self.__a
			self.__ua, self.__ub = self.__ub, self.__ua

		if T:
			if T > datetime.datetime.now(datetime.timezone.utc):
				raise ValueError('timestamp is in the future')
			self.__t = T
		else:
			self.__t = now()

		assert isinstance(M, int)
		assert isinstance(PB, float)
		assert isinstance(PF, float)
		assert isinstance(PD, float)
		self.__maxsize = M
		self.__pb = PB
		self.__pf = PF
		self.__pd = PD

		self.__sigod = OrderedDict()
		self.__sigod['A'] = self.__a.pubkeyData()
		self.__sigod['B'] = self.__b.pubkeyData()
		self.__sigod['T'] = self.__t
		self.__sigod['M'] = self.__maxsize
		self.__sigod['PB'] = PB
		self.__sigod['PF'] = PF
		self.__sigod['PD'] = PD

		if self.__ua:
			self.__sigod['UA'] = self.__ua
			sigdata_a = BSON.encode(self.__sigod)
			del self.__sigod['UA']
		else:
			sigdata_a = BSON.encode(self.__sigod)

		if self.__ub:
			self.__sigod['UB'] = self.__ub
			sigdata_b = BSON.encode(self.__sigod)
			del self.__sigod['UB']
		else:
			sigdata_b = BSON.encode(self.__sigod)

		self.__sa = SA
		if SA == None:
			if self.__a.hasPrivkey():
				self.__sa = self.__a.signAddress(sigdata_a)
		else:
			verify = self.__a.verifyAddress(SA, sigdata_a)
			if not verify:
				raise InvalidDataException('SA signature verify failed.')

		self.__sb = SB
		if SB == None:
			if self.__b.hasPrivkey():
				self.__sb = self.__b.signAddress(sigdata_b)
		else:
			verify = self.__b.verifyAddress(SB, sigdata_b)
			if not verify:
				raise InvalidDataException('SA signature verify failed.')

		if not (self.__sa or self.__sb):
			raise ValueError('Invalid signatures.')

		pow_data = BSON.encode(self.__sigod)
		if P == True:
			self.pow_done = None
			def setpow(nonce):
				self.__pow = nonce
				if self.pow_done:
					self.pow_done()
			calculatePOW(message=pow_data, difficulty=3.0, callback=setpow)
		else:
			self.__pow = P
			if isinstance(P, int):
				if not checkPOW(message=pow_data, difficulty=3.0, nonce=P):
					raise ValueError('Invalid POW')
			elif P != None:
				raise ValueError('Invalid POW value')

		self.__d = D
		self.__ds = DS

		if self.__d:
			if self.__d > datetime.datetime.now(datetime.timezone.utc):
				raise ValueError('Deleted timestamp is in the future.')
			if self.__d < self.__t:
				raise ValueError('Deleted timestamp is older than timestamp.')
			if not self.isComplete:
				raise ValueError('Deleted path may not be incomplete.')

			self.__sigod['SA'] = self.__sa
			self.__sigod['SB'] = self.__sb
			self.__sigod['D'] = self.__d
			sigdata = BSON.encode(self.__sigod)
			verify = self.__a.verifyAddress(self.__ds, sigdata) or self.__b.verifyAddress(self.__ds, sigdata)
			if not verify:
				raise InvalidDataException('DS signature verify failed.')

		self.__hash = hash(self.AHash + self.BHash)
		self.__longhash = hashlib.sha256(BSON.encode(self.__sigod)).digest()
Exemple #13
0
class A2MXPath():
	def __init__(self, A=None, B=None, T=None, UA=None, UB=None, M=None, PB=None, PF=None, PD=None, P=None, SA=None, SB=None, D=None, DS=None):
		# A = node A public key data (address, sign and encrypt compressed public keys)
		# B = node B public key data
		# T = timestamp
		# UA = AX URI node A
		# UB = AX URI node B
		# M = MaxSize
		# PB = POW broadcast
		# PF = POW forward
		# PD = POW direct
		# P = path proof of work
		# SA = node A signature (over A, B, T and UA if present)
		# SB = node B signature (over A, B, T and UB if present)
		# D = deleted timestamp
		# DS = deleted signature (over A, B, T, SA, SB and D)
		# A must always be the smaller binary value

		if not isinstance(A, ECC):
			self.__a = ECC(pubkey_data=A)
		else:
			self.__a = A
			if not SA and self.__a.hasPrivkey() and not PB < 0:
				UA = config['publish_axuri']

		if not isinstance(B, ECC):
			self.__b = ECC(pubkey_data=B)
		else:
			self.__b = B
			if not SB and self.__b.hasPrivkey() and not PB < 0:
				UB = config['publish_axuri']

		def testURI(uri):
			if uri == None:
				return
			if len(uri) > 32:
				raise ValueError('URI too long')
			try:
				host, port = uri.split(':')
			except ValueError:
				raise ValueError('Invalid URI')
			try:
				int(port)
			except ValueError:
				raise ValueError('Invalid URI')
			validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789'
			if not all(c in validChars for c in host):
				raise ValueError('Invalid chars in URI')
		testURI(UA)
		testURI(UB)

		self.__ua = UA
		self.__ub = UB

		if self.__a.pubkeyData() > self.__b.pubkeyData():
			self.__a, self.__b = self.__b, self.__a
			self.__ua, self.__ub = self.__ub, self.__ua

		if T:
			if T > datetime.datetime.now(datetime.timezone.utc):
				raise ValueError('timestamp is in the future')
			self.__t = T
		else:
			self.__t = now()

		assert isinstance(M, int)
		assert isinstance(PB, float)
		assert isinstance(PF, float)
		assert isinstance(PD, float)
		self.__maxsize = M
		self.__pb = PB
		self.__pf = PF
		self.__pd = PD

		self.__sigod = OrderedDict()
		self.__sigod['A'] = self.__a.pubkeyData()
		self.__sigod['B'] = self.__b.pubkeyData()
		self.__sigod['T'] = self.__t
		self.__sigod['M'] = self.__maxsize
		self.__sigod['PB'] = PB
		self.__sigod['PF'] = PF
		self.__sigod['PD'] = PD

		if self.__ua:
			self.__sigod['UA'] = self.__ua
			sigdata_a = BSON.encode(self.__sigod)
			del self.__sigod['UA']
		else:
			sigdata_a = BSON.encode(self.__sigod)

		if self.__ub:
			self.__sigod['UB'] = self.__ub
			sigdata_b = BSON.encode(self.__sigod)
			del self.__sigod['UB']
		else:
			sigdata_b = BSON.encode(self.__sigod)

		self.__sa = SA
		if SA == None:
			if self.__a.hasPrivkey():
				self.__sa = self.__a.signAddress(sigdata_a)
		else:
			verify = self.__a.verifyAddress(SA, sigdata_a)
			if not verify:
				raise InvalidDataException('SA signature verify failed.')

		self.__sb = SB
		if SB == None:
			if self.__b.hasPrivkey():
				self.__sb = self.__b.signAddress(sigdata_b)
		else:
			verify = self.__b.verifyAddress(SB, sigdata_b)
			if not verify:
				raise InvalidDataException('SA signature verify failed.')

		if not (self.__sa or self.__sb):
			raise ValueError('Invalid signatures.')

		pow_data = BSON.encode(self.__sigod)
		if P == True:
			self.pow_done = None
			def setpow(nonce):
				self.__pow = nonce
				if self.pow_done:
					self.pow_done()
			calculatePOW(message=pow_data, difficulty=3.0, callback=setpow)
		else:
			self.__pow = P
			if isinstance(P, int):
				if not checkPOW(message=pow_data, difficulty=3.0, nonce=P):
					raise ValueError('Invalid POW')
			elif P != None:
				raise ValueError('Invalid POW value')

		self.__d = D
		self.__ds = DS

		if self.__d:
			if self.__d > datetime.datetime.now(datetime.timezone.utc):
				raise ValueError('Deleted timestamp is in the future.')
			if self.__d < self.__t:
				raise ValueError('Deleted timestamp is older than timestamp.')
			if not self.isComplete:
				raise ValueError('Deleted path may not be incomplete.')

			self.__sigod['SA'] = self.__sa
			self.__sigod['SB'] = self.__sb
			self.__sigod['D'] = self.__d
			sigdata = BSON.encode(self.__sigod)
			verify = self.__a.verifyAddress(self.__ds, sigdata) or self.__b.verifyAddress(self.__ds, sigdata)
			if not verify:
				raise InvalidDataException('DS signature verify failed.')

		self.__hash = hash(self.AHash + self.BHash)
		self.__longhash = hashlib.sha256(BSON.encode(self.__sigod)).digest()

	def __getstate__(self):
		state = { 'A': self.__a.pubkeyData(), 'B': self.__b.pubkeyData(), 'T': self.__t, 'SA': self.__sa, 'SB': self.__sb, 'M': self.__maxsize, 'PB': self.__pb, 'PF': self.__pf, 'PD': self.__pd }
		if self.__ua:
			state['UA'] = self.__ua
		if self.__ub:
			state['UB'] = self.__ub
		if self.__pow:
			state['P'] = self.__pow
		if self.__d:
			state['D'] = self.__d
			state['DS'] = self.__ds
		return state

	def __setstate__(self, state):
		return A2MXPath.__init__(self, **state)

	def __hash__(self):
		return self.__hash

	@property
	def longHash(self):
		return self.__longhash

	@property
	def isComplete(self):
		return self.__sa != None and self.__sb != None and self.__pow != None

	@property
	def data(self):
		return self.__getstate__()

	@property
	def A(self):
		return self.__a.pubkeyData()
	@property
	def AHash(self):
		return self.__a.pubkeyHash()
	@property
	def AURI(self):
		return self.__ua

	@property
	def B(self):
		return self.__b.pubkeyData()
	@property
	def BHash(self):
		return self.__b.pubkeyHash()
	@property
	def BURI(self):
		return self.__ub

	@property
	def deleted(self):
		return self.__d

	@property
	def timestamp(self):
		return self.__t
	@property
	def newest_timestamp(self):
		return self.__d or self.__t

	def markdelete(self):
		assert self.__d == None
		assert 'SA' not in self.__sigod
		assert 'SB' not in self.__sigod
		assert 'D' not in self.__sigod

		self.__sigod['SA'] = self.__sa
		self.__sigod['SB'] = self.__sb
		self.__d = now()
		self.__sigod['D'] = self.__d
		sigdata = BSON.encode(self.__sigod)
		if self.__a.hasPrivkey():
			self.__ds = self.__a.signAddress(sigdata)
		elif self.__b.hasPrivkey():
			self.__ds = self.__b.signAddress(sigdata)
		else:
			raise ValueError('Cannot mark path as deleted without private key.')

	def __eq__(self, other):
		if not isinstance(other, A2MXPath):
			return False
		return self.A == other.A and self.B == other.B

	def equal(self, other):
		if not isinstance(other, A2MXPath):
			return False
		return self.data == other.data

	def otherHash(self, otherHash):
		if otherHash == self.AHash:
			return self.BHash
		elif otherHash == self.BHash:
			return self.AHash
		raise ValueError('otherHash is neither A or B.')

	def ecc(self, h):
		if h == self.AHash:
			return self.A
		if h == self.BHash:
			return self.B
		raise ValueError("Hash is neither A nor B")

	@property
	def hashes(self):
		return (self.AHash, self.BHash)

	def is_better_than(self, other):
		if self != other:
			raise ValueError('Cannot compare paths with different nodes')
		return self.newest_timestamp > other.newest_timestamp

	def __str__(self):
		return 'A: {}{} B: {}{} Timestamp: {} M: {} PB: {} PF: {} PD: {} POW: {} Deleted: {}{}'.format(
			self.__a.pubkeyHashBase58(), " ({})".format(self.__ua) if self.__ua else "",
			self.__b.pubkeyHashBase58(), " ({})".format(self.__ub) if self.__ub else "",
			self.__t.isoformat(),
			self.__maxsize, self.__pb, self.__pf, self.__pd, self.__pow,
			self.__d.isoformat() if self.__d else False,
			"" if self.isComplete else " Incomplete")
Exemple #14
0
class A2MXNode():
	def __init__(self, selectloop):
		self.selectloop = selectloop
		self.pathlist = a2mxpath.PathList()

		self.streams = []
		self.ecc = ECC(keyfile=config['keyfile'])
		self.connected_nodes = { self.ecc.pubkeyHash(): self }

		mypub = self.ecc.pubkeyHashBase58()
		if sys.stdout.isatty():
			cwd = os.getcwd().rsplit('/', 1)[1]
			sys.stdout.write("\x1b]2;{}: {}\x07".format(cwd, mypub))
		print("I am", mypub)

		self.selectloop.tadd(90, self.find_new_peers)

	def add(self, selectable):
		self.selectloop.add(selectable)
	def remove(self, selectable):
		self.selectloop.remove(selectable)
	def wadd(self, selectable):
		self.selectloop.wadd(selectable)
	def wremove(self, selectable):
		self.selectloop.wremove(selectable)

	def add_stream(self, stream):
#		if stream.remote_ecc.pubkeyHash() in self.connected_nodes:
#			return False
		assert stream not in self.streams
		self.streams.append(stream)
		self.connected_nodes[stream.remote_ecc.pubkeyHash()] = stream
		return True

	def del_stream(self, stream):
		if stream not in self.streams:
			return
		self.streams.remove(stream)
		assert stream.remote_ecc.pubkeyHash() in self.connected_nodes and self.connected_nodes[stream.remote_ecc.pubkeyHash()] == stream
		del self.connected_nodes[stream.remote_ecc.pubkeyHash()]

		if stream.path and stream.path.isComplete:
			self.del_path(stream.path)

	def new_path(self, path, stream=None):
		fromhash = stream.remote_ecc.pubkeyHashBase58() if stream else 'myself'
		if self.pathlist.new(path, fromhash):
			self.send_path(path, stream)
		print(self.pathlist)

	def send_path(self, path, stream=None):
		for ostream in self.streams:
			if ostream == stream:
				continue
			if hasattr(ostream, 'forward'):
				ostream.forward.sendCall('path', path.data)

	def del_path(self, path):
		self.pathlist.delete(path)
		self.send_path(path)

	def update_nodes(self, path):
		def up(h):
			if h not in self.nodes:
				self.nodes[h] = []
			nl = self.nodes[h]
			if path in nl:
				nl.remove(path)
			nl.append(path)
		up(path.AHash)
		up(path.BHash)

	def sendto(self, node, data):
		if node == self.ecc.pubkeyHash():
			data = self.ecc.decrypt(bytes(data))
			self.request.parseRequest(data)
			return

#		if node not in self.connected_nodes:
#			try:
#				a2mxaccess.A2MXAccessStore(node, data)
#				print("stored data for {}".format(ECC.b58(node)))
#			except a2mxaccess.A2MXAccessException:
#				print("cannot send to node {}".format(ECC.b58(node)))
#			return False
		self.connected_nodes[node].raw_send(data)
		return True

	def find_new_peers(self):
		self.selectloop.tadd(random.randint(30, 90), self.find_new_peers)

		if len(self.streams) >= config['connections']:
			return

		try:
			new_hash = random.sample(self.pathlist.axuris.keys() - self.connected_nodes.keys(), 1)[0]
		except ValueError:
			return
		A2MXStream(self, uri='ax://' + self.pathlist.axuris[new_hash], pubkey_hash=new_hash)

	def find_routes_from(self, src, dst, maxhops=None):
		if dst not in self.nodes:
			return []
		pathlist = self.nodes[dst]
		if len(pathlist) == 0:
			return []
		dst_pubc = pathlist[0].pubkeyCompressed(dst)

		def find_path(pathlist, lasthop, thispath=None, step=1):
			if maxhops != None and step >= maxhops:
				return
			if thispath == None:
				thispath = [dst_pubc]
			for path in pathlist:
				if path.deleted:
					continue
				nexthop = path.otherHash(lasthop)
				nexthop_pubc = path.pubkeyCompressed(nexthop)
				if nexthop == src:
					ytp = thispath[:]
					ytp.append(nexthop_pubc)
					yield A2MXRoute(ytp)
					continue
				if nexthop == dst:
					continue
				if nexthop_pubc in thispath:
					continue
				tp = thispath[:]
				tp.append(nexthop_pubc)
				for p in find_path(self.nodes[lasthop], lasthop, tp, step+1):
					yield p
		return find_path(pathlist, dst)

	def shortest_route(self, src, dst):
		try:
			routes = [ x for x in self.find_routes_from(src, dst) ]
		except KeyError:
			return A2MXRoute([])
		if len(routes) == 0:
			return A2MXRoute([])
		return min(routes, key=len)
Exemple #15
0
	def __str__(self):
		s = 'A2MXRoute'
		for r in self.routes:
			s += ' ' + ECC.b58(r).decode('ascii')
		return s