Esempio n. 1
0
	def bind(self):
		AuthenticatedTransport.bind(self)
		self._adapter.SetProperty('Discoverable', self._address is None, signature='sv')
		self._serverSocket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
		self._serverSocket.bind(('', bluetooth.PORT_ANY))
		self._serverSocket.listen(self['socketConnectionBacklog'])
		bluetooth.advertise_service(self._serverSocket, 'kozo', service_id=self._uuid, service_classes=[self._uuid, bluetooth.SERIAL_PORT_CLASS], profiles=[bluetooth.SERIAL_PORT_PROFILE])
		infoTransport(self, 'Bound on port', self._serverSocket.getsockname()[1], 'with UUID', self._uuid)
Esempio n. 2
0
	def getUnauthenticatedSocket(self, otherTransport, addressIndex, address):
		targetAddress, targetUuid = address
		serviceMatches = bluetooth.find_service(name='kozo', address=targetAddress, uuid=targetUuid)
		infoTransport(self, 'Found', len(serviceMatches), 'services matches for UUID', targetUuid, '/ Address', targetAddress)
		if not len(serviceMatches):
			return None
		sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
		sock.connect((serviceMatches[0]['host'], serviceMatches[0]['port']))
		return _BluetoothSocketWrapper(sock)
Esempio n. 3
0
	def connect(self, otherTransport):
		assert self._privateKey is not None
		selfNode = self.getNode()
		selfName = selfNode.getName()
		remoteNode = otherTransport.getNode()
		remoteName = remoteNode.getName()
		remoteAddress = (otherTransport['address'], otherTransport['onionPort'])
		try:
			sock = socket.create_connection(remoteAddress, kozoConfig('connectionRetry'))
			# First, send magic knock-knock message.
			sock.sendall(self.MAGIC_KNOCKKNOCK + struct.pack('=H', len(selfName)) + selfName.encode('utf8'))
			# We expect to be asked to sign a message of length up to 32K characters.
			signLength = struct.unpack('=H', self._readLoop(sock.recv, struct.calcsize('=H')))[0]
			if signLength > self.MAX_INITIAL_SIGN_LENGTH:
				raise KozoError('Initial signature length too long:', signLength)
			# Get message to sign.
			initialToSign = self._readLoop(sock.recv, signLength)
			# Expand it.
			selfDate = int(time.time())
			selfRandom = self._genRandom()
			actualToSign = self.ACTUAL_SIGN_FORMAT.format(
				initial=initialToSign,
				date=str(selfDate).encode('utf8'),
				server=remoteName.encode('utf8'),
				client=selfName.encode('utf8'),
				random=selfRandom
			)
			# Sign it. Some versions of Paramiko take a random pool here, others don't. Try both.
			try:
				signedMessage = self._privateKey.sign_ssh_data(Crypto.Random.new(), actualToSign)
			except TypeError:
				signedMessage = self._privateKey.sign_ssh_data(actualToSign)
			signed = bytes(signedMessage)
			# Send signed response back.
			sock.sendall(struct.pack('=Qii', selfDate, len(selfRandom), len(signed)) + selfRandom + signed)
			# Expect acknowledgement.
			acknowledgement = self._readLoop(sock.recv, len(self.SIGN_OK))
			if acknowledgement != self.SIGN_OK:
				raise KozoError('Invalid acknowledgement.')
			# We're all clear.
			return OnionChannel(selfNode, remoteNode, sock)
		except BaseException as e:
			infoTransport(self, 'Failed to connect to', remoteAddress, e, printTraceback=False)
Esempio n. 4
0
	def accept(self):
		infoTransport(self, 'Waiting for a connection')
		assert self._serverSocket is not None
		selfNode = self.getNode()
		selfName = selfNode.getName()
		connection = self._serverSocket.accept()[0]
		try:
			connection.settimeout(kozoConfig('connectionRetry'))
			# Parse header.
			knockHeader = self._readLoop(connection.recv, len(self.MAGIC_KNOCKKNOCK))
			if knockHeader != self.MAGIC_KNOCKKNOCK:
				raise KozoError('Invalid header received')
			nodeNameLength = struct.unpack('=H', self._readLoop(connection.recv, struct.calcsize('=H')))[0]
			if nodeNameLength > self._maxNodeNameLength:
				raise KozoError('Node length field larger than largest-named node defined in the system:', nodeNameLength)
			remoteName = self._readLoop(connection.recv, nodeNameLength).decode('utf8')
			remoteNode = kozoSystem().getNodeByName(remoteName)
			if remoteNode is None:
				raise KozoError('Unknown node name received', repr(remoteName))
			remoteKey = paramiko.RSAKey(data=base64.b64decode(remoteNode.getPublicKey()[1]))
			# Generate message to sign.
			selfDate = int(time.time())
			selfRandom = self._genRandom()
			initialToSign = self.INITIAL_SIGN_FORMAT.format(
				date=str(selfDate).encode('utf8'),
				server=selfName.encode('utf8'),
				client=remoteName.encode('utf8'),
				random=selfRandom
			)
			# Send it.
			connection.sendall(struct.pack('=H', len(initialToSign)) + initialToSign)
			# Get and verify signed response.
			remoteDate, remoteRandomLength, remoteSignedLength = struct.unpack('=Qii', self._readLoop(connection.recv, struct.calcsize('=Qii')))
			if abs(remoteDate - selfDate) > self.MAX_DATE_DELTA:
				raise KozoError('Clocks differ by', abs(remoteDate - selfDate), 'seconds; rejecting message')
			if remoteRandomLength < self.RANDOM_LENGTH[0]:
				raise KozoError('Remote random string is shorter than minimum of', self.RANDOM_LENGTH[0], 'bytes')
			if remoteRandomLength > self.RANDOM_LENGTH[1]:
				raise KozoError('Remote random string is longer than maximum of', self.RANDOM_LENGTH[1], 'bytes')
			remoteRandom = self._readLoop(connection.recv, remoteRandomLength)
			actualToSign = self.ACTUAL_SIGN_FORMAT.format(
				initial=initialToSign,
				date=str(remoteDate).encode('utf8'),
				server=selfName.encode('utf8'),
				client=remoteName.encode('utf8'),
				random=remoteRandom
			)
			if len(actualToSign) > self.MAX_ACTUAL_SIGN_LENGTH:
				raise KozoError('Message to sign is too long:', len(actualToSign), 'bytes while max is', self.MAX_ACTUAL_SIGN_LENGTH)
			if remoteSignedLength > len(actualToSign) * self.SIGNED_MAX_EXPANSION_FACTOR:
				raise KozoError('Signed message is too long:', remoteSignedLength, 'bytes while max is', len(actualToSign) * self.SIGNED_MAX_EXPANSION_FACTOR)
			actualSigned = paramiko.Message(self._readLoop(connection.recv, remoteSignedLength))
			if not remoteKey.verify_ssh_sig(actualToSign, actualSigned):
				raise KozoError('Invalid signature received')
			# Send acknowledgement.
			connection.sendall(self.SIGN_OK)
			# We're clear.
			return OnionChannel(remoteNode, selfNode, connection)
		except BaseException:
			try:
				connection.close()
			except:
				pass
			raise