def __init__(self, type, data={}): self._content = { 'type': type, 'timestamp': time.time(), 'sender': kozoSystem().getSelfNode().getName(), 'data': data } self._size = None
def init(self): if self['incomingOnly'] and self['outgoingOnly']: raise KozoError('Cannot have both incomingOnly and outgoingOnly enabled.') if self['outgoingOnly'] and self['address']: raise KozoError('Cannot provide both outgoingOnly and address.') if not self['outgoingOnly']: if not self['address']: raise KozoError('Must provide .onion address.') if not self['address'].endswith('.onion'): raise KozoError('Address must end in .onion.') if self['onionPort'] is None: self['onionPort'] = self['port'] self._serverSocket = None self._maxNodeNameLength = max(len(node.getName()) for node in kozoSystem().getNodes()) if self._maxNodeNameLength + self.RANDOM_LENGTH[1] > self.MAX_INITIAL_SIGN_LENGTH / 2: raise KozoError('Some Node in the system has a name that is too long for the onion Transport to work.')
def getSender(self): return kozoSystem().getNodeByName(self._content['sender'])
def getRecipientNodes(self): return kozoSystem().getNodes()
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