def parse_certs_cell(self, cell): circid, cell_content = self.decode_circid(cell) if circid != 0 or cell_content[0] != COMMAND_CERTS: raise ORError("Malformed CERTS cell") cert_num = struct.unpack("!B", cell_content[3])[0] cell_content = cell_content[4:] certs = [] for _ in range(cert_num): if len(cell_content) < 3: raise ORError("Malformed CERTS cell") cert_type = struct.unpack("!B", cell_content[0])[0] length = struct.unpack("!H", cell_content[1:3])[0] if len(cell_content) < length + 3: raise ORError("Malformed CERTS cell") certs.append((cert_type, cell_content[3:length + 3])) cell_content = cell_content[length + 3:] return certs
def read_from_conn(self): data = self.conn.recv(4096) if not data: raise ORError("Connection is closed") self.buf += data
def add_circid(self, circid, content): if self.circid_len == 2: packed_circid = struct.pack("!H", circid) elif self.circid_len == 4: packed_circid = struct.pack("!I", circid) else: raise ORError("Unsupported CircID length") return packed_circid + content
def parse_versions_cell(self, cell): circid, cell_content = self.decode_circid(cell) if circid != 0 or cell_content[0] != COMMAND_VERSIONS: raise ORError("Malformed VERSIONS cell") cell_content = cell_content[3:] if len(cell_content) % 2 != 0: raise ORError("Malformed VERSIONS cell") versions = [] for i in range(len(cell_content) / 2): versions.append( struct.unpack("!H", cell_content[i * 2:i * 2 + 2])[0]) return versions
def process_versions_cell(self, versions_cell): versions = self.parse_versions_cell(versions_cell) common_versions = set(versions) & self.our_versions if not common_versions: raise ORError("Unable to negotiate a common version") self.version = max(common_versions) if self.version >= 4: self.circid_len = 4 else: self.circid_len = 2
def encrypt_onion_skin(content_no_digest, key, direction='f'): if direction == 'f': Dfunc = key.Dffunc Kfunc = key.Kffunc elif direction == 'b': Dfunc = key.Dbfunc Kfunc = key.Kbfunc else: raise ORError("Unrecognized direction {}".format(direction)) Dfunc.update(content_no_digest) digest = Dfunc.digest()[:4] content = content_no_digest[0:5] + digest + content_no_digest[9:] payload = Kfunc.encrypt(content) return payload
def decode_circid(self, cell): if not self.circid_len: circid_len = 2 else: circid_len = self.circid_len if circid_len == 2: circid = struct.unpack("!H", cell[:circid_len])[0] elif circid_len == 4: circid = struct.unpack("!I", cell[:circid_len])[0] else: raise ORError("Unsupported CircID length") cell_content = cell[circid_len:] return (circid, cell_content)
def relay_forward(self, circid, cell_content): with self.lock: if circid not in self.circuits_client: return (FINISHED, self.client_or_conn.destroy_cell( circid, ERROR_DESTROYED), None) if circid not in self.map_client_to_server: self.destroy(circid) return (FINISHED, self.client_or_conn.destroy_cell( circid, ERROR_DESTROYED), None) circid_server = self.map_client_to_server[circid] if circid_server not in self.circuits_server: self.destroy(circid) return (FINISHED, self.client_or_conn.destroy_cell( circid, ERROR_DESTROYED), None) key_client = self.circuits_client[circid] key_server = self.circuits_server[circid_server] command = cell_content[0] payload = cell_content[1:] content = key_client.Kffunc.decrypt(payload) if content[1:3] != "\x00\x00": # not recognized new_payload = key_server.Kffunc.encrypt(content) return (FINISHED, None, self.server_or_conn.add_circid(circid_server, command + new_payload)) temp_Dffunc = key_client.Dffunc.copy() cell_digest = content[5:9] content_no_digest = content[0:5] + "\x00\x00\x00\x00" + content[9:] temp_Dffunc.update(content_no_digest) computed_digest = temp_Dffunc.digest()[:4] if cell_digest == computed_digest: # recognized key_client.Dffunc = temp_Dffunc streamid = struct.unpack("!H", content[3:5])[0] if content[0] == RELAY_DATA and key_client.has_dir_stream( streamid): length = struct.unpack("!H", content[9:11])[0] if length > MAX_DATA_LEN: raise ORError("Incorrect length in RELAY cell") data = content[11:11 + length] if data.startswith(GET_AUTHORITY_Z): # a quick and dirty way return (INJECTED, streamid, circid, circid_server) elif content[0] == RELAY_BEGIN_DIR: key_client.add_dir_stream(streamid) new_payload = encrypt_onion_skin(content_no_digest, key_server, direction='f') return (FINISHED, None, self.server_or_conn.add_circid(circid_server, command + new_payload)) else: new_payload = key_server.Kffunc.encrypt(content) return (FINISHED, None, self.server_or_conn.add_circid(circid_server, command + new_payload))
def process_netinfo_cell(self, netinfo_cell): # TODO: actual decoding and parsing circid, cell_content = self.decode_circid(netinfo_cell) if circid != 0 or cell_content[0] != COMMAND_NETINFO: raise ORError("Malformed NETINFO cell")
def process_auth_challenge_cell(self, auth_challenge_cell): # TODO: actual decoding and parsing circid, cell_content = self.decode_circid(auth_challenge_cell) if circid != 0 or cell_content[0] != COMMAND_AUTH_CHALLENGE: raise ORError("Malformed AUTH_CHALLENGE cell")