def report_fields(self): msg = Messages() time = self.issue.strftime("%d/%m/%Y às %H:%M") header = parse_string(msg.reports[self.header], { "name": self.city + ", " + self.name, "font": self.font }) foot_note = parse_string( msg.reports["foot_notice"], {"foot_notice": choice(msg.reports["foot_notes"])}) footer = parse_string(msg.reports[self.footer], { "name": self.name, "font": self.font, "time": time }) body = "" attrs = { i.var_name: i for i in self.field_map if not i.hs_notification and i.var_name in self.__dict__ } for attr in attrs: if attr in self.__dict__ and self.__dict__[attr]: if attrs[attr].is_warning: body += f"{self.__dict__[attr]}\n" else: body += f"```{attrs[attr].name}:``` _{self.__dict__[attr]}{attrs[attr].final_character}_ {attrs[attr].notation}\n" if not body: body = "\n⚠ Não foi possível coletar nenhum dado para esta região... Um aviso foi enviado para a Equipe de Desenvolvimento.\n" return [header, body, foot_note, footer]
def test_command_data_cleanup(self): """ All data from the previous command must be cleared when reading the next one. Those tests check that the last bare "p" command should be using the default values for all the data fields. """ # a1, a2, bang, slash, pattern, replacement, flags, outf script = ["/a1/I, /a2/M ! s/foo/bar/igw file", "p"] p_data = parse_string("\n".join(script))[-1] self._assert_defaults(p_data, skip=None, msg=script) # int_arg script = ["q99", "p"] p_data = parse_string("\n".join(script))[-1] self._assert_defaults(p_data, skip=None, msg=script) # fname script = ["r file", "p"] p_data = parse_string("\n".join(script))[-1] self._assert_defaults(p_data, skip=None, msg=script) # label_name script = [":label", "p"] p_data = parse_string("\n".join(script))[-1] self._assert_defaults(p_data, skip=None, msg=script) # cmd_txt script = ["a", "text", "p"] p_data = parse_string("\n".join(script))[-1] self._assert_defaults(p_data, skip=None, msg=script)
def test_string_expr_count(self): """Calling compile_string() should increase by one the expression count.""" before = sedparse.cur_input.string_expr_count _ = parse_string("p") _ = parse_string("p") _ = parse_string("p") after = sedparse.cur_input.string_expr_count self.assertEqual(before + 3, after)
def _get_verifying_key(self, host_key_blob): # Parse the received data from the host_key_blob index, host_key_type = parse_string(host_key_blob, 0) index, curve_name = parse_string(host_key_blob, index) index, host_public_key = parse_string(host_key_blob, index) # Find the expected host key in ~/.ssh/known_hosts expected_host_key_type = None expected_host_public_key = None known_hosts_filename = os.path.expanduser('~/.ssh/known_hosts') for line in open(known_hosts_filename, 'r'): if len(line.strip()) > 0: current_hostname, current_key_type, current_key = line.split(' ') if current_hostname == self.hostname: expected_host_key_type = current_key_type expected_host_public_key = current_key.decode('base64') break # If we *did* find the host key (i.e. we've already connected to this server), check that # everything matches if expected_host_key_type is not None: assert host_key_type == expected_host_key_type, 'Unexpected host key type: %s' % host_key_type assert curve_name == 'nistp256', 'Unknown curve name: %s' % curve_name assert host_key_blob == expected_host_public_key, \ 'Unexpected host public key: %s' % repr(host_key_blob) # Otherwise, if we haven't seen the host key before, prompt the user to see if they're okay with # that else: assert host_key_type == 'ecdsa-sha2-nistp256', 'Unknown host key type: %s' % host_key_type key_fingerprint = hashlib.sha256(host_key_blob).digest().encode('base64') # Remove the base64-added new lines, and the padding '=' characters key_fingerprint = key_fingerprint.replace('\n', '').rstrip('=') print "The authenticity of host '%s' can't be established." % self.hostname print "ECDSA key fingerprint is SHA256:%s." % key_fingerprint answer = raw_input("Are you sure you want to continue connecting (yes/no)?\n").strip() while answer not in ['yes', 'no', '']: answer = raw_input("Please type 'yes' or 'no': ").strip() # Add key to ~/.ssh/known_hosts if answer == 'yes': with open(known_hosts_filename, 'a') as known_hosts_file: host_key_base64 = host_key_blob.encode('base64').replace('\n', '') known_hosts_file.write('%s %s %s\n' % (self.hostname, host_key_type, host_key_base64)) else: assert False, 'Host key verification failed.' # NFI why we need to skip a byte here - I can't find this format documented anywhere. I assume # this is some kind of type indicator. assert host_public_key[0] == '\x04' return ecdsa.VerifyingKey.from_string(host_public_key[1:], curve=ecdsa.NIST256p)
def test_commands_with_text(self): for command in ("a", "i", "c", "e"): for script, text in TEST_DATA[command]: parsed = parse_string(script)[0] self.assertEqual(command, parsed.cmd, msg=script) self.assertEqual(text, str(parsed.x.cmd_txt), msg=script) self._assert_defaults(parsed, skip=["cmd_txt"], msg=script)
def test_commands_with_no_args(self): commands = ( "=", "d", "D", "F", "g", "G", "h", "H", "l", "L", "n", "N", "p", "P", "q", "Q", "x", "z", ) for command in commands: for template in ("%s", "%s;", "{%s}", "%s#foo", "{ \t%s \t}"): script = template % command parsed = parse_string(script) parsed = parsed[1] if "{" in script else parsed[0] self.assertEqual(command, parsed.cmd, msg=script) self._assert_defaults(parsed, skip=None, msg=script)
def test_blank_lines(self): # sedparse extension for data in TEST_DATA["\n"]: script = data[0] expected_commands = list(data[1:]) self.assertEqual(expected_commands, [x.cmd for x in parse_string(script)], msg=script)
def parse_char(expression, target): if target is None: target = [get_temp()] used_temps.extend(target) v = parse_string(expression.value) return f'scoreboard players set {target[0]} {NAMESPACE} {ord(v)}\n', Int( ), target
def test_str_struct_misc(self): """Check str for struct_{regex,replacement,output}""" script = "s/foo/bar/igw file" parsed = parse_string(script) self.assertEqual("/foo/igw", str(parsed[0].x.cmd_subst.regx)) self.assertEqual("bar", str(parsed[0].x.cmd_subst.replacement)) self.assertEqual("file", str(parsed[0].x.cmd_subst.outf))
def hs_notification_fields(self): msg = Messages() header = parse_string(msg.hs_notifications[self.header], { "name": self.name, "font": self.font }) footer = msg.hs_notifications["footer"] body = "" attrs = { i.var_name: i for i in self.field_map if i.hs_notification and i.var_name in self.__dict__ } for attr in attrs: if attrs[attr].is_warning: body += f"{self.__dict__[attr]}" for attr in attrs: if not attrs[attr].is_warning: body += f"{attrs[attr].name}:``` _{self.__dict__[attr]}{attrs[attr].final_character}_ {attrs[attr].notation}" if not body: body = "\n⚠ Não foi possível gerar nenhum aviso. Uma notificação foi enviada para a Equipe de Desenvolvimento.\n" return [header, body, footer]
def report_fields(self): msg = Messages() time = self.issue.strftime("%d/%m/%Y às %H:%M") header = parse_string(msg.reports[self.header], { "name": self.name, "font": self.font }) foot_note = parse_string( msg.reports["foot_notice"], {"foot_notice": choice(msg.reports["foot_notes"])}) footer = parse_string(msg.reports[self.footer], { "name": self.name, "font": self.font, "time": time }) body = "" attrs = { i.var_name: i for i in self.field_map if i.var_name in self.__dict__ } for attr in attrs: if attr in self.__dict__ and self.__dict__[attr]: if attrs[attr].is_warning: body += f"{self.__dict__[attr]}\n" else: body += f"```{attrs[attr].name}:``` _{self.__dict__[attr]}{attrs[attr].final_character}_ {attrs[attr].notation}\n" if not body: body = "\n⚠ Não foi possível coletar nenhum dado para esta região... Um aviso foi enviado para a Equipe de Desenvolvimento.\n" config = Config() for warning in config.warnings: if "reports" in warning["display"]: if warning['place'][0] == self.state and ( warning['place'][1] == self.name if warning['place'][1] else True): effective_start = datetime.strptime( warning["effective_since"], "%d/%m/%Y %H:%M") effective_until = datetime.strptime( warning["effective_until"], "%d/%m/%Y %H:%M") if datetime.now() >= effective_start and datetime.now( ) <= effective_until: foot_note = parse_string( msg.reports["foot_notice"], {"foot_notice": warning["message"]}) return [header, body, foot_note, footer]
def test_ignore_trailing_fluff(self): for script_end in ("", "\n"): # empty=EOF for data in TEST_DATA["trailing_fluff"]: script = data[0] + script_end expected_commands = list(data[1:]) self.assertEqual(expected_commands, [x.cmd for x in parse_string(script)], msg=script)
def test_commands_with_filename(self): for command in ("r", "R", "w", "W"): for script_end in ("", "\n"): # empty=EOF for script, filename in TEST_DATA[command]: script = script + script_end parsed = parse_string(script)[0] self.assertEqual(command, parsed.cmd, msg=script) self.assertEqual(filename, parsed.x.fname, msg=script) self._assert_defaults(parsed, skip=["fname"], msg=script)
def test_comments(self): # sedparse extension command = "#" for script_end in ("", "\n"): # empty=EOF for index, script, comment in TEST_DATA[command]: script = script + script_end parsed = parse_string(script)[index] self.assertEqual(command, parsed.cmd, msg=script) self.assertEqual(comment, parsed.x.comment, msg=script) self._assert_defaults(parsed, skip=["comment"], msg=script)
def test_global_data_cleanup(self): """ Global variables should be reset after an error and after the end of normal execution, to avoid leaking and affecting the next run. """ # After compile_string(), prog.* should be NULL script = ["p", "x", "s/foo/bar/g"] sedparse.compile_string([], "\n".join(script)) self.assertIsNone(sedparse.prog.base) self.assertIsNone(sedparse.prog.cur) self.assertIsNone(sedparse.prog.end) self.assertIsNone(sedparse.prog.text) # After compile_file(), prog.file should be NULL script = ["p", "x", "s/foo/bar/g"] with tempfile.NamedTemporaryFile(mode="w", delete=False) as file: file.write("\n".join(script)) filename = file.name sedparse.compile_file([], filename) self.assertIsNone(sedparse.prog.file) os.remove(filename) # After normal execution, blocks is back to zero and old_text_buf will # still hold some contents. script = ["{", "i\\", "foo", "}"] _ = parse_string("\n".join(script)) self.assertEqual(0, sedparse.blocks) self.assertIsNone(sedparse.pending_text) # After an error, every global should be reset script = ["{", "i\\", "foo", "XXX"] try: _ = parse_string("\n".join(script)) except sedparse.ParseError: pass self.assertEqual(0, sedparse.blocks) self.assertIsNone(sedparse.old_text_buf) self.assertIsNone(sedparse.pending_text) self.assertIsNone(sedparse.prog.base) self.assertIsNone(sedparse.prog.cur) self.assertIsNone(sedparse.prog.end) self.assertIsNone(sedparse.prog.text) self.assertIsNone(sedparse.prog.file)
def tasks_sender(task_list): for task in task_list[::-1]: if task['tags']: search_body = set(parse_string(task['title']) + task['tags']) else: search_body = parse_string(task['title']) relevant_users = db_handler.get_relevant_users_ids(search_body) if relevant_users: logger.debug(f"Found task {task['link'], task['tags']} for the users {relevant_users}") for user_id in relevant_users: logger.debug(f"Sending task {task['link']}") tags = ', '.join(task['tags']) if task['tags'] else '' price_usd = '<i>(~ ' + '{:,}'.format(round(task['price_usd'])).replace(',', ' ') + '$)</i>' if task[ 'price_usd'] else '' price = ' '.join([str(task['price']), task['currency'], price_usd]) if task['price_format'] == 'per_hour': price += ' <i>за час</i>' text = f"<b>{task['title']}</b>\n{price}\n<code>{tags}</code>" resp = bot.send_message(text, link=task['link'], chat_id=user_id, disable_preview=True) if resp in [400, 403]: logger.warning(f"Bot was kicked from the chat {user_id}. Deactivating user.") db_handler.change_user_status(user_id)
def test_repr_struct_misc(self): """Check repr for struct_{regex,replacement,output}""" script = "s/foo/bar/igw file" parsed = parse_string(script) self.assertEqual( "struct_regex(slash='/', pattern='foo', flags='igw')", repr(parsed[0].x.cmd_subst.regx), ) self.assertEqual("struct_replacement(text='bar')", repr(parsed[0].x.cmd_subst.replacement)) self.assertEqual("struct_output(name='file')", repr(parsed[0].x.cmd_subst.outf))
def flag_report_fields(self): if hasattr(self, "flag"): msg = Messages() time = self.issue.strftime("%d/%m/%Y às %H:%M") header = parse_string(msg.reports[self.header], { "name": self.name, "font": self.font }) foot_note = parse_string( msg.reports["foot_notice"], {"foot_notice": choice(msg.reports["foot_notes"])}) footer = parse_string(msg.reports[self.footer], { "name": self.name, "font": self.font, "time": time }) body = f"```Bandeira Atual:``` {self.flag}\n" + ( f"```Bandeira Anterior:``` {self.old_flag}\n" if hasattr( self, "old_flag") else "") config = Config() for warning in config.warnings: if "reports" in warning["display"]: if warning['place'][0] == self.state and ( warning['place'][1] == self.name if warning['place'][1] else True): effective_start = datetime.strptime( warning["effective_since"], "%d/%m/%Y %H:%M") effective_until = datetime.strptime( warning["effective_until"], "%d/%m/%Y %H:%M") if datetime.now() >= effective_start and datetime.now( ) <= effective_until: foot_note = warning["message"] return [header, body, foot_note, footer]
def test_address(self): for script, bang, addr1, addr2 in TEST_DATA["address"]: expected = [bang, addr1, addr2] # only the first command matters, i.e., { when {} parsed = parse_string(script)[0] self.assertListEqual( expected, [ parsed.addr_bang, str(parsed.a1) if parsed.a1 else None, str(parsed.a2) if parsed.a2 else None, ], msg=script, )
def test_str_struct_addr(self): """Check str for all kinds of addresses""" data = [ ("$", "$", "None"), ("91,92", "91", "92"), ("91~992", "91~992", "None"), ("91,~992", "91", "~992"), ("91,+992", "91", "+992"), ("/foo/IM", "/foo/IM", "None"), ] for address, str_a1, str_a2 in data: script = address + "p" parsed = parse_string(script) self.assertEqual(str_a1, str(parsed[0].a1), msg=script) self.assertEqual(str_a2, str(parsed[0].a2), msg=script)
def test_commands_with_label(self): for command in (":", "b", "t", "T", "v"): for script_end in ("", "\n"): # empty=EOF for script, label in TEST_DATA[command]: script = script + script_end parsed = parse_string(script) if parsed[0].cmd == "{": parsed = parsed[1] else: parsed = parsed[0] self.assertEqual(command, parsed.cmd, msg=script) self.assertEqual(label, parsed.x.label_name, msg=script) self._assert_defaults(parsed, skip=["label_name"], msg=script)
def users(): if request.method == 'POST': if request.form: user = request.form['user'] keywords = parse_string(request.form['keywords'], sep=",") db_handler.update_user_keys(user, keywords) return redirect(url_for('users')) elif request.json: user_id = request.json.get('user_id') stl = log_parser.get_sent_tasks_list(user_id=user_id) return jsonify(stl) sm = log_parser.get_sent_messages() users = db_handler.get_users() return render_template('users.html', title='Users stats', sm=sm, users=users)
def do_GET(self): self.send_response(200) self.send_header('Content-type', 'application/json') self.end_headers() path_part_URI = self.path print(path_part_URI) if path_part_URI == "/favicon.ico": print("Отработан запрос к серверу favicon") print() else: try: from_currency, to_currency, ante = parse_string(path_part_URI) except: # любые ошибки вызова .parse_string print() print("Ошибка парсинга Query-компонента URI") report = {"response code": "0", "error": "URI parsing error"} else: absolute_URL = construct_url(BASE_URL, APIKEY, from_currency, (to_currency)) print() print(f"Приложение запрашивает ресурс: \n{absolute_URL}") rate = get_rate(absolute_URL, from_currency, to_currency) if isinstance(rate, float): total = ante * rate report = { "response code": "200", "currency exchange": f"{from_currency} to {to_currency}", "requested value": f"{ante}", "rate": f"{rate}", "exchange result": f"{total:.2f}", "error": "" } else: # возникла ошибка при запросе к API обмена валют code = rate[0] text = rate[1] report = {"response code": f"{code}", "error": f"{text}"} finally: # при любых ошибках отправляет ответ пользователю print() print("Результат запроса ресурса:") response = json.dumps(report, ensure_ascii=False).encode("utf-8") print(response.decode()) print() self.wfile.write(response)
def test_commands_y_and_s(self): for command in ("y", "s"): for script, delimiter, arg1, arg2 in TEST_DATA[command]: parsed = parse_string(script)[0] self.assertEqual(command, parsed.cmd, msg=script) self.assertEqual(delimiter, parsed.x.cmd_subst.regx.slash, msg=script) self.assertEqual(arg1, parsed.x.cmd_subst.regx.pattern, msg=script) self.assertEqual(arg2, parsed.x.cmd_subst.replacement.text, msg=script) self._assert_defaults(parsed, skip=["slash", "pattern", "replacement"], msg=script)
def read(self): '''Read data from the remote server. This data will be encrypted, and its authenticity guaranteed (both client-to-server and server-to-client). Returns (string): the data sent by the remote server. ''' data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, channel_data = parse_string(data, index) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_DATA'] assert recipient_channel == self._local_channel_number return channel_data
def test_commands_with_numeric_arg(self): # Note that those commands "solo", with no numeric arguments, # are already tested in test_commands_with_no_args(). for command in ("l", "L", "q", "Q"): for arg in (0, 5, 99): for template in ( "%s%d", "%s%d;", "{%s%d}", "%s%d#foo", "{ \t%s \t%d \t}", ): script = template % (command, arg) parsed = parse_string(script) parsed = parsed[1] if "{" in script else parsed[0] self.assertEqual(command, parsed.cmd, msg=script) self.assertEqual(arg, parsed.x.int_arg, msg=script) self._assert_defaults(parsed, skip=["int_arg"], msg=script)
def test_repr_struct_addr(self): """Check repr for all kinds of addresses""" data = [ # pylint: disable=line-too-long ( "$", "struct_addr(addr_type=7, addr_number=0, addr_step=0, addr_regex=None)", "None", ), ( "91,92", "struct_addr(addr_type=3, addr_number=91, addr_step=0, addr_regex=None)", "struct_addr(addr_type=3, addr_number=92, addr_step=0, addr_regex=None)", ), ( "91~992", "struct_addr(addr_type=4, addr_number=91, addr_step=992, addr_regex=None)", "None", ), ( "91,~992", "struct_addr(addr_type=3, addr_number=91, addr_step=0, addr_regex=None)", "struct_addr(addr_type=6, addr_number=0, addr_step=992, addr_regex=None)", ), ( "91,+992", "struct_addr(addr_type=3, addr_number=91, addr_step=0, addr_regex=None)", "struct_addr(addr_type=5, addr_number=0, addr_step=992, addr_regex=None)", ), ( "/foo/IM", "struct_addr(addr_type=2, addr_number=0, addr_step=0," " addr_regex=struct_regex(slash='/', pattern='foo', flags='IM'))", "None", ), ] for address, repr_a1, repr_a2 in data: script = address + "p" parsed = parse_string(script) self.assertEqual(repr_a1, repr(parsed[0].a1), msg=script) self.assertEqual(repr_a2, repr(parsed[0].a2), msg=script)
def test_full(self): script = "p" output = { "a1": None, "a2": None, "addr_bang": False, "cmd": "p", "x": { "cmd_txt": { "text": [] }, "int_arg": -1, "fname": "", "cmd_subst": { "regx": { "pattern": "", "flags": "", "slash": "" }, "replacement": { "text": "" }, "outf": { "name": "" }, }, "label_name": "", "comment": "", }, "line": 1, } parsed = parse_string(script) self.assertEqual(output, parsed[0].to_dict(remove_empty=False), msg=script) self.assertEqual(output, json.loads(parsed[0].to_json(remove_empty=False)), msg=script)
def parse_string_const(copy_strings, expression, target): if copy_strings: a = eval('b' + expression.value) code = '' a = list(a) # + [0] - initalized to 0 anyway, no need to specify this code += f'scoreboard players operation $index {NAMESPACE} = {target[0]} {NAMESPACE}\n' for co, c in enumerate(a): code += f'scoreboard players set $value {NAMESPACE} {c}\n' code += f'function {NAMESPACE}:set_heap\n' code += f'scoreboard players add $index {NAMESPACE} 1\n' return code, Pointer(Int()), target else: a = parse_string(expression.value).encode() # a = eval('b' + expression.value) if target is None: target = [get_temp()] used_temps.extend(target) code = f'scoreboard players set {target[0]} {NAMESPACE} {get_position()}\n' a = list(a) + [0] stringss[get_position()] = a register_space(len(a)) return code, Pointer(Int()), target
def disconnect(self): '''Cleanly close the connection to the remote server.''' # Send our exit status msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_REQUEST'])) msg.append(generate_uint32(self._remote_channel_number)) msg.append(generate_string('exit-status')) msg.append(generate_byte(0)) # False msg.append(generate_uint32(0)) # Exit status = 0 self._ssh_transport_connection.send(''.join(msg)) # Then close the channel msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_CLOSE'])) msg.append(generate_uint32(self._remote_channel_number)) self._ssh_transport_connection.send(''.join(msg)) # Read back the remote side's exit status data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, request_type = parse_string(data, index) index, want_reply_byte = parse_byte(data, index) want_reply = want_reply_byte != 0 index, exit_status = parse_uint32(data, index) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_REQUEST'] assert recipient_channel == self._local_channel_number assert request_type == 'exit-status' assert not want_reply # Disconnect at the transport layer self._ssh_transport_connection.disconnect() return exit_status
def test_command_s_flags(self): command = "s" for script, delimiter, pattern, replacement, flags, flag_arg in TEST_DATA[ "s-flags"]: parsed = parse_string(script)[0] self.assertEqual(command, parsed.cmd, msg=script) self.assertEqual(delimiter, parsed.x.cmd_subst.regx.slash, msg=script) self.assertEqual(pattern, parsed.x.cmd_subst.regx.pattern, msg=script) self.assertEqual(replacement, parsed.x.cmd_subst.replacement.text, msg=script) self.assertEqual(flags, parsed.x.cmd_subst.regx.flags, msg=script) self.assertEqual(flag_arg, parsed.x.cmd_subst.outf.name, msg=script) self._assert_defaults( parsed, skip=["slash", "pattern", "replacement", "flags", "outf"], msg=script, )
def _run_diffie_hellman_group14_sha1_key_exchange(self, server_kex_init, client_kex_init): # q, g, and p from https://tools.ietf.org/html/rfc3526#section-3 q = 2 ** 2048 g = 2 p = int(''' 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E34 04DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F4 06B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8 FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E 462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2 261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF '''.replace(' ', '').replace('\n', ''), 16) x = random.SystemRandom().randint(2, q - 1) e = pow(g, x, p) # Send public key to server msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_KEXDH_INIT'])) msg.append(generate_mpint(e)) self.send(''.join(msg)) # Receive (K_S || f || s) from the server # i.e. host key blob, f, and the signature, from the server msg = self.read() index, ssh_msg_type = parse_byte(msg, 0) assert ssh_msg_type == SSH_MSG_NUMS['SSH_MSG_KEXDH_REPLY'] index, host_key_blob = parse_string(msg, index) index, f = parse_mpint(msg, index) index, signature = parse_string(msg, index) # Calculate a verifying key from the host key blob verifying_key = self._get_verifying_key(host_key_blob) # Also calculate the shared key, exchange hash, and session ID (this is the same as the exchange # hash) shared_key = pow(f, x, p) hashed_data = \ generate_string(self._client_id_string.strip('\r\n')) + \ generate_string(self._server_id_string.strip('\r\n')) + \ generate_string(client_kex_init) + \ generate_string(server_kex_init) + \ generate_string(host_key_blob) + \ generate_mpint(e) + \ generate_mpint(f) + \ generate_mpint(shared_key) exchange_hash = hashlib.sha1(hashed_data).digest() self.session_id = exchange_hash # Pull out the signature blob from the message index, ecdsa_identifier = parse_string(signature, 0) assert ecdsa_identifier == SERVER_HOST_KEY_ALGORITHM, \ 'Unknown signature type: %s' % ecdsa_identifier index, signature_blob = parse_string(signature, index) index, r = parse_mpint(signature_blob, 0) index, s = parse_mpint(signature_blob, index) # Verify that the signature on the message is correct assert verifying_key.verify( get_32_byte_repr(r) + get_32_byte_repr(s), exchange_hash, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_string ) print colors.cyan('Signature validated correctly! OMG!') # Derive *all* the keys! key_derivation_options = { 'shared_key': shared_key, 'exchange_hash': exchange_hash, 'session_id': self.session_id, } # Client to server keys (these hard-coded ASCII letters brought to you by the RFC's key # derivation function: https://tools.ietf.org/html/rfc4253#section-7.2) initial_iv_client_to_server = _derive_encryption_key( key_derivation_options, 'A', AES_BLOCK_LEN) ctr = Counter.new( AES_BLOCK_LEN * 8, initial_value=int(initial_iv_client_to_server.encode('hex'), AES_BLOCK_LEN)) encryption_key_client_to_server = _derive_encryption_key( key_derivation_options, 'C', AES_BLOCK_LEN) self._aes_client_to_server = AES.new(encryption_key_client_to_server, AES.MODE_CTR, counter=ctr) self._integrity_key_client_to_server = _derive_encryption_key(key_derivation_options, 'E') # Server to client keys initial_iv_server_to_client = _derive_encryption_key( key_derivation_options, 'B', AES_BLOCK_LEN) ctr = Counter.new( AES_BLOCK_LEN * 8, initial_value=int(initial_iv_server_to_client.encode('hex'), AES_BLOCK_LEN)) encryption_key_server_to_client = _derive_encryption_key( key_derivation_options, 'D', AES_BLOCK_LEN) self._aes_server_to_client = AES.new(encryption_key_server_to_client, AES.MODE_CTR, counter=ctr) self._integrity_key_server_to_client = _derive_encryption_key(key_derivation_options, 'F')
def _do_user_auth(self): # Ask the server whether it supports doing SSH user auth msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_SERVICE_REQUEST'])) msg.append(generate_string(SSH_USERAUTH_STRING)) self._ssh_transport_connection.send(''.join(msg)) # Check that it says yes data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) assert msg_type == SSH_MSG_NUMS['SSH_MSG_SERVICE_ACCEPT'], \ 'Unknown message type received: %d' % msg_type index, service_name = parse_string(data, index) assert service_name == SSH_USERAUTH_STRING print colors.cyan("Let's do ssh-userauth!") # Ask the server which authentication methods it supports msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_USERAUTH_REQUEST'])) msg.append(generate_string(self.username.encode('utf-8'))) msg.append(generate_string('ssh-connection')) msg.append(generate_string('none')) self._ssh_transport_connection.send(''.join(msg)) # Check that publickey is one of them data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, supported_auth_methods = parse_name_list(data, index) index, partial_success_byte = parse_byte(data, index) partial_success = partial_success_byte != 0 assert msg_type == SSH_MSG_NUMS['SSH_MSG_USERAUTH_FAILURE'], \ 'Unknown message type: %d' % msg_type assert 'publickey' in supported_auth_methods, \ 'Server does not support public key authentication' assert not partial_success # Try to public key auth rsa_key = RSA.importKey(open(self.keyfile)) pkcs_key = PKCS1_v1_5.new(rsa_key) msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_USERAUTH_REQUEST'])) msg.append(generate_string(self.username.encode('utf-8'))) msg.append(generate_string('ssh-connection')) msg.append(generate_string('publickey')) msg.append(generate_byte(1)) # True: we really do want to authenticate msg.append(generate_string('ssh-rsa')) msg.append(generate_string( generate_string('ssh-rsa') + generate_mpint(rsa_key.e) + generate_mpint(rsa_key.n) )) # Desperately try to figure out how signing works in this silly encapsulating protocol signed_data = generate_string(self._ssh_transport_connection.session_id) + ''.join(msg) # OMG Pycrypto, did it have to be *your* SHA1 implementation? signature = pkcs_key.sign(SHA.new(signed_data)) msg.append(generate_string(generate_string('ssh-rsa') + generate_string(signature))) # Send the public key auth message to the server self._ssh_transport_connection.send(''.join(msg)) data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) assert msg_type == SSH_MSG_NUMS['SSH_MSG_USERAUTH_SUCCESS'], \ 'Unknown message type: %d' % msg_type print colors.cyan('Successfully user authed!')
def _create_ssh_connection(self): # Read the global request that SSH sends us - this is trying to let us know all host keys, but # it's OpenSSH-specific, and we don't need it data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, request_name = parse_string(data, index) index, want_reply_byte = parse_byte(data, index) want_reply = want_reply_byte != 0 assert msg_type == SSH_MSG_NUMS['SSH_MSG_GLOBAL_REQUEST'] assert request_name == '*****@*****.**' assert not want_reply # Reply to let OpenSSH know that we don't know what they're talking about msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_REQUEST_FAILURE'])) self._ssh_transport_connection.send(''.join(msg)) # Actually get started with opening a channel for SSH communication window_size = 1048576 maximum_packet_size = 16384 # Request to open a session channel msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_OPEN'])) msg.append(generate_string('session')) msg.append(generate_uint32(self._local_channel_number)) msg.append(generate_uint32(window_size)) msg.append(generate_uint32(maximum_packet_size)) self._ssh_transport_connection.send(''.join(msg)) # Check that a channel was opened successfully data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, self._remote_channel_number = parse_uint32(data, index) index, initial_window_size = parse_uint32(data, index) index, maximum_packet_size = parse_uint32(data, index) print colors.cyan('Message type: %d' % msg_type) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_OPEN_CONFIRMATION'] assert recipient_channel == self._local_channel_number print colors.cyan('Remote channel number: %d' % self._remote_channel_number) print colors.cyan('Initial window size: %d' % initial_window_size) print colors.cyan('Maximum window size: %d' % maximum_packet_size) # Ask to turn that session channel into a shell msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_REQUEST'])) msg.append(generate_uint32(self._remote_channel_number)) msg.append(generate_string('shell')) msg.append(generate_byte(1)) # True, we do want a reply here self._ssh_transport_connection.send(''.join(msg)) # OpenSSH then asks to increase their window size, that's fine, do it data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, bytes_to_add = parse_uint32(data, index) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_WINDOW_ADJUST'] initial_window_size += bytes_to_add # Check that they tell us they've opened a channel successfully data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_SUCCESS'] assert recipient_channel == self._local_channel_number print colors.cyan('Successfully opened shell!')