def authenticate(self, message_hash, app_hash, key_handle): """Sign a message with a security key using CTAP version 1""" self._poll() if self.dev.error == 'nocred': raise ApduError(APDU.WRONG_DATA, b'') elif self.dev.error == 'err': raise ApduError(0, b'') flags, counter, sig = self._sign(message_hash, app_hash, key_handle, SSH_SK_USER_PRESENCE_REQD) return Byte(flags) + UInt32(counter) + sig
def process_packet(self, data): """Process an incoming packet""" packet = SSHPacket(data) pkttype = packet.get_byte() if pkttype == MSG_USERAUTH_REQUEST: _ = packet.get_string() # username _ = packet.get_string() # service method = packet.get_string() if self._auth: self._auth.cancel() if self._override_gss_mech: self.send_packet(Byte(MSG_USERAUTH_GSSAPI_RESPONSE), String('mismatch')) elif self._override_pk_ok: self.send_packet(Byte(MSG_USERAUTH_PK_OK), String(''), String('')) else: self._auth = lookup_server_auth(self, 'user', method, packet) else: self._auth.process_packet(pkttype, packet)
async def test_get_sk_keys(self): """Test getting U2F security keys""" key = asyncssh.generate_private_key( '*****@*****.**') cert = key.generate_user_certificate(key, 'test') mock_agent = _Agent(Byte(SSH_AGENT_IDENTITIES_ANSWER) + UInt32(2) + String(key.public_data) + String('') + String(cert.public_data) + String('')) await mock_agent.start('mock_agent') async with asyncssh.connect_agent('mock_agent') as agent: await agent.get_keys() await mock_agent.stop()
def _sign(message_hash, app_hash, key_handle, flags): """Sign a message with a security key""" alg, public_value, private_value = der_decode(key_handle) if alg == SSH_SK_ECDSA: key = ECDSAPrivateKey.construct( b'nistp256', public_value, int.from_bytes(private_value, 'big')) else: key = EdDSAPrivateKey.construct(b'ed25519', private_value) counter = 0x12345678 sig = key.sign(app_hash + Byte(flags) + UInt32(counter) + message_hash) return flags, counter, sig
async def test_add_sk_keys(self): """Test adding U2F security keys""" key = asyncssh.generate_private_key( '*****@*****.**') cert = key.generate_user_certificate(key, 'test') mock_agent = _Agent(Byte(SSH_AGENT_SUCCESS)) await mock_agent.start('mock_agent') async with asyncssh.connect_agent('mock_agent') as agent: for keypair in asyncssh.load_keypairs([key, (key, cert)]): async with agent: self.assertIsNone(await agent.add_keys([keypair])) async with agent: with self.assertRaises(asyncssh.KeyExportError): await agent.add_keys([key.convert_to_public()]) await mock_agent.stop()
async def test_add_keys_failure(self, agent): """Test getting keys from the agent""" os.mkdir('.ssh', 0o700) key = asyncssh.generate_private_key('ssh-rsa') key.write_private_key(Path('.ssh', 'id_rsa')) try: mock_agent = _Agent(Byte(SSH_AGENT_FAILURE)) await mock_agent.start('mock_agent') async with asyncssh.connect_agent('mock_agent') as agent: async with agent: await agent.add_keys() async with agent: with self.assertRaises(ValueError): await agent.add_keys([key]) finally: os.remove(os.path.join('.ssh', 'id_rsa')) os.rmdir('.ssh')
def communicate(self, request): """Process SSH key signing request""" # pylint: disable=no-self-use packet = SSHPacket(request) request = packet.get_string() packet.check_end() packet = SSHPacket(request) version = packet.get_byte() _ = packet.get_uint32() # sock_fd data = packet.get_string() packet.check_end() if version == 0: return b'', b'' elif version == 1: return b'', b'invalid request' else: skey = asyncssh.load_keypairs('skey')[0] sig = skey.sign(data) return String(Byte(KEYSIGN_VERSION) + String(sig)), b''
async def test_errors(self): """Test getting error responses from SSH agent""" key = asyncssh.generate_private_key('ssh-rsa') keypair = asyncssh.load_keypairs(key)[0] for response in (None, b'', Byte(SSH_AGENT_FAILURE), b'\xff'): mock_agent = _Agent(response) await mock_agent.start('mock_agent') async with asyncssh.connect_agent('mock_agent') as agent: for request in (agent.get_keys(), agent.sign(b'xxx', b'test'), agent.add_keys([key]), agent.add_smartcard_keys('xxx'), agent.remove_keys([keypair]), agent.remove_smartcard_keys('xxx'), agent.remove_all(), agent.lock('passphrase'), agent.unlock('passphrase')): async with agent: with self.assertRaises(ValueError): await request await mock_agent.stop()
def process_packet(self, data): """Process an incoming packet""" packet = SSHPacket(data) pkttype = packet.get_byte() if pkttype == MSG_USERAUTH_REQUEST: _ = packet.get_string() # username _ = packet.get_string() # service method = packet.get_string() if self._auth: self._auth.cancel() if self._override_pk_ok: self.send_packet(Byte(MSG_USERAUTH_PK_OK), String(''), String('')) else: self._auth = lookup_server_auth(self, 'user', method, packet) else: try: self._auth.process_packet(pkttype, packet) except DisconnectError as exc: self.connection_lost(exc)
def simulate_ecdh_reply(self, host_key_data, server_pub, sig): """Simulate receiving ab ECDH reply packet""" self.process_packet(b''.join( (Byte(MSG_KEX_ECDH_REPLY), String(host_key_data), String(server_pub), String(sig))))
def simulate_ecdh_init(self, client_pub): """Simulate receiving an ECDH init packet""" self.process_packet(Byte(MSG_KEX_ECDH_INIT) + String(client_pub))
def simulate_gss_complete(self, f, sig): """Simulate receiving a GSS complete packet""" self.process_packet(b''.join((Byte(MSG_KEXGSS_COMPLETE), MPInt(f), String(sig), Boolean(False))))
def simulate_dh_gex_reply(self, host_key_data, f, sig): """Simulate receiving a DH GEX reply packet""" self.process_packet(b''.join( (Byte(MSG_KEX_DH_GEX_REPLY), String(host_key_data), MPInt(f), String(sig))))
def simulate_dh_gex_init(self, e): """Simulate receiving a DH GEX init packet""" self.process_packet(Byte(MSG_KEX_DH_GEX_INIT) + MPInt(e))
def simulate_dh_gex_group(self, p, g): """Simulate receiving a DH GEX group packet""" self.process_packet(Byte(MSG_KEX_DH_GEX_GROUP) + MPInt(p) + MPInt(g))
def simulate_rsa_done(self, sig): """Simulate receiving an RSA done packet""" self.process_packet(Byte(MSG_KEXRSA_DONE) + String(sig))
def simulate_rsa_pubkey(self, host_key_data, trans_key_data): """Simulate receiving an RSA pubkey packet""" self.process_packet( Byte(MSG_KEXRSA_PUBKEY) + String(host_key_data) + String(trans_key_data))
def _begin_session(self, stdin, stdout, stderr): """Begin processing a new session""" # pylint: disable=too-many-statements action = stdin.channel.get_command() or stdin.channel.get_subsystem() if not action: action = 'echo' if action == 'echo': yield from echo(stdin, stdout, stderr) elif action == 'conn_close': yield from stdin.read(1) stdout.write('\n') self._conn.close() elif action == 'close': yield from stdin.read(1) stdout.write('\n') elif action == 'agent': agent = yield from asyncssh.connect_agent(self._conn) if agent: stdout.write(str(len((yield from agent.get_keys()))) + '\n') agent.close() else: stdout.channel.exit(1) elif action == 'rejected_agent': chan = SSHAgentChannel(self._conn, asyncio.get_event_loop(), None, 1, 32768) try: yield from chan.open(SSHUNIXStreamSession) except asyncssh.ChannelOpenError: stdout.channel.exit(1) elif action == 'rejected_session': chan = SSHClientChannel(self._conn, asyncio.get_event_loop(), None, 1, 32768) try: yield from chan.create(SSHClientStreamSession, None, None, {}, None, None, None, False) except asyncssh.ChannelOpenError: stdout.channel.exit(1) elif action == 'rejected_tcpip_direct': chan = self._conn.create_tcp_channel() try: yield from chan.connect(SSHTCPStreamSession, '', 0, '', 0) except asyncssh.ChannelOpenError: stdout.channel.exit(1) elif action == 'unknown_tcpip_listener': chan = self._conn.create_tcp_channel() try: yield from chan.accept(SSHTCPStreamSession, 'xxx', 0, '', 0) except asyncssh.ChannelOpenError: stdout.channel.exit(1) elif action == 'invalid_tcpip_listener': chan = self._conn.create_tcp_channel() try: yield from chan.accept(SSHTCPStreamSession, b'\xff', 0, '', 0) except asyncssh.ChannelOpenError: stdout.channel.exit(1) elif action == 'rejected_unix_direct': chan = self._conn.create_unix_channel() try: yield from chan.connect(SSHUNIXStreamSession, '') except asyncssh.ChannelOpenError: stdout.channel.exit(1) elif action == 'unknown_unix_listener': chan = self._conn.create_unix_channel() try: yield from chan.accept(SSHUNIXStreamSession, 'xxx') except asyncssh.ChannelOpenError: stdout.channel.exit(1) elif action == 'invalid_unix_listener': chan = self._conn.create_unix_channel() try: yield from chan.accept(SSHUNIXStreamSession, b'\xff') except asyncssh.ChannelOpenError: stdout.channel.exit(1) elif action == 'late_auth_banner': try: self._conn.send_auth_banner('auth banner') except OSError: stdin.channel.exit(1) elif action == 'invalid_open_confirm': stdin.channel.send_packet(MSG_CHANNEL_OPEN_CONFIRMATION, UInt32(0), UInt32(0), UInt32(0)) elif action == 'invalid_open_failure': stdin.channel.send_packet(MSG_CHANNEL_OPEN_FAILURE, UInt32(0), String(''), String('')) elif action == 'env': value = stdin.channel.get_environment().get('TEST', '') stdout.write(value + '\n') elif action == 'term': chan = stdin.channel info = str((chan.get_terminal_type(), chan.get_terminal_size(), chan.get_terminal_mode(asyncssh.PTY_OP_OSPEED))) stdout.write(info + '\n') elif action == 'xon_xoff': stdin.channel.set_xon_xoff(True) elif action == 'no_xon_xoff': stdin.channel.set_xon_xoff(False) elif action == 'signals': try: yield from stdin.readline() except asyncssh.BreakReceived as exc: stdin.channel.exit_with_signal('ABRT', False, str(exc.msec)) except asyncssh.SignalReceived as exc: stdin.channel.exit_with_signal('ABRT', False, exc.signal) except asyncssh.TerminalSizeChanged as exc: size = (exc.width, exc.height, exc.pixwidth, exc.pixheight) stdin.channel.exit_with_signal('ABRT', False, str(size)) elif action == 'exit_status': stdin.channel.exit(1) elif action == 'closed_status': stdin.channel.close() stdin.channel.exit(1) elif action == 'exit_signal': stdin.channel.exit_with_signal('ABRT', False, 'exit_signal') elif action == 'closed_signal': stdin.channel.close() stdin.channel.exit_with_signal('ABRT', False, 'closed_signal') elif action == 'invalid_exit_signal': stdin.channel.exit_with_signal('invalid') elif action == 'invalid_exit_lang': stdin.channel.exit_with_signal('ABRT', False, '', 'invalid') elif action == 'window_after_close': stdin.channel.send_packet(MSG_CHANNEL_CLOSE) stdin.channel.send_packet(MSG_CHANNEL_WINDOW_ADJUST, UInt32(0)) elif action == 'empty_data': stdin.channel.send_packet(MSG_CHANNEL_DATA, String('')) elif action == 'partial_unicode': data = '\xff\xff'.encode('utf-8') stdin.channel.send_packet(MSG_CHANNEL_DATA, String(data[:3])) stdin.channel.send_packet(MSG_CHANNEL_DATA, String(data[3:])) elif action == 'partial_unicode_at_eof': data = '\xff\xff'.encode('utf-8') stdin.channel.send_packet(MSG_CHANNEL_DATA, String(data[:3])) elif action == 'unicode_error': stdin.channel.send_packet(MSG_CHANNEL_DATA, String(b'\xff')) elif action == 'data_past_window': stdin.channel.send_packet(MSG_CHANNEL_DATA, String(2 * 1025 * 1024 * '\0')) elif action == 'data_after_eof': stdin.channel.send_packet(MSG_CHANNEL_EOF) stdout.write('xxx') elif action == 'data_after_close': yield from asyncio.sleep(0.1) stdout.write('xxx') elif action == 'ext_data_after_eof': stdin.channel.send_packet(MSG_CHANNEL_EOF) stdin.channel.write_stderr('xxx') elif action == 'invalid_datatype': stdin.channel.send_packet(MSG_CHANNEL_EXTENDED_DATA, UInt32(255), String('')) elif action == 'double_eof': stdin.channel.send_packet(MSG_CHANNEL_EOF) stdin.channel.write_eof() elif action == 'double_close': yield from asyncio.sleep(0.1) stdout.write('xxx') stdin.channel.send_packet(MSG_CHANNEL_CLOSE) elif action == 'request_after_close': stdin.channel.send_packet(MSG_CHANNEL_CLOSE) stdin.channel.exit(1) elif action == 'unexpected_auth': self._conn.send_packet(Byte(MSG_USERAUTH_REQUEST), String('guest'), String('ssh-connection'), String('none')) elif action == 'invalid_response': stdin.channel.send_packet(MSG_CHANNEL_SUCCESS) else: stdin.channel.exit(255) stdin.channel.close() yield from stdin.channel.wait_closed()
def simulate_rsa_secret(self, encrypted_k): """Simulate receiving an RSA secret packet""" self.process_packet(Byte(MSG_KEXRSA_SECRET) + String(encrypted_k))
def send_userauth_failure(self, partial_success): """Send a user authentication failure response""" self._auth = None self.send_packet(Byte(MSG_USERAUTH_FAILURE), NameList([]), Boolean(partial_success))
def test_gss_errors(self): """Unit test error conditions in GSS key exchange""" client_conn, server_conn = \ _KexClientStub.make_pair(b'gss-group1-sha1-mech', '3') with self.subTest('Init sent to client'): with self.assertRaises(asyncssh.ProtocolError): client_conn.process_packet(Byte(MSG_KEXGSS_INIT)) with self.subTest('Complete sent to server'): with self.assertRaises(asyncssh.ProtocolError): server_conn.process_packet(Byte(MSG_KEXGSS_COMPLETE)) with self.subTest('Exchange failed to complete'): with self.assertRaises(asyncssh.ProtocolError): client_conn.simulate_gss_complete(1, b'succeed') with self.subTest('Error sent to server'): with self.assertRaises(asyncssh.ProtocolError): server_conn.process_packet(Byte(MSG_KEXGSS_ERROR)) client_conn.close() server_conn.close() with self.subTest('Signature verification failure'): with self.assertRaises(asyncssh.KeyExchangeFailed): yield from self._check_kex(b'gss-group1-sha1-mech', '0,fail') with self.subTest('Empty token in init'): with self.assertRaises(asyncssh.ProtocolError): yield from self._check_kex(b'gss-group1-sha1-mech', '0,empty_init') with self.subTest('Empty token in continue'): with self.assertRaises(asyncssh.ProtocolError): yield from self._check_kex(b'gss-group1-sha1-mech', '1,empty_continue') with self.subTest('Token after complete'): with self.assertRaises(asyncssh.ProtocolError): yield from self._check_kex(b'gss-group1-sha1-mech', '0,continue_token') for steps in range(2): with self.subTest('Token after complete', steps=steps): with self.assertRaises(asyncssh.ProtocolError): yield from self._check_kex(b'gss-group1-sha1-mech', str(steps) + ',extra_token') with self.subTest('Context not secure'): with self.assertRaises(asyncssh.ProtocolError): yield from self._check_kex(b'gss-group1-sha1-mech', '1,no_server_integrity') with self.subTest('GSS error'): with self.assertRaises(asyncssh.KeyExchangeFailed): yield from self._check_kex(b'gss-group1-sha1-mech', '1,step_error') with self.subTest('GSS error with error token'): with self.assertRaises(asyncssh.KeyExchangeFailed): yield from self._check_kex(b'gss-group1-sha1-mech', '1,step_error,errtok')
def send_userauth_success(self): """Send a user authentication success response""" self._auth = None self.send_packet(Byte(MSG_USERAUTH_SUCCESS))