def _parse_kexecdh_init(self, m): Q_C_bytes = m.get_string() self.Q_C = ec.EllipticCurvePublicNumbers.from_encoded_point( self.curve, Q_C_bytes) K_S = self.transport.get_server_key().asbytes() K = self.P.exchange(ec.ECDH(), self.Q_C.public_key(default_backend())) K = long(hexlify(K), 16) # compute exchange hash hm = Message() hm.add(self.transport.remote_version, self.transport.local_version, self.transport.remote_kex_init, self.transport.local_kex_init) hm.add_string(K_S) hm.add_string(Q_C_bytes) # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion hm.add_string(self.Q_S.public_numbers().encode_point()) hm.add_mpint(long(K)) H = self.hash_algo(hm.asbytes()).digest() self.transport._set_K_H(K, H) sig = self.transport.get_server_key().sign_ssh_data(H) # construct reply m = Message() m.add_byte(c_MSG_KEXECDH_REPLY) m.add_string(K_S) m.add_string(self.Q_S.public_numbers().encode_point()) m.add_string(sig) self.transport._send_message(m) self.transport._activate_outbound()
def _parse_kexecdh_reply(self, m): K_S = m.get_string() Q_S_bytes = m.get_string() self.Q_S = ec.EllipticCurvePublicNumbers.from_encoded_point( self.curve, Q_S_bytes ) sig = m.get_binary() K = self.P.exchange(ec.ECDH(), self.Q_S.public_key(default_backend())) K = long(hexlify(K), 16) # compute exchange hash and verify signature hm = Message() hm.add( self.transport.local_version, self.transport.remote_version, self.transport.local_kex_init, self.transport.remote_kex_init, ) hm.add_string(K_S) # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion hm.add_string(self.Q_C.public_numbers().encode_point()) hm.add_string(Q_S_bytes) hm.add_mpint(K) self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest()) self.transport._verify_key(K_S, sig) self.transport._activate_outbound()
def _parse_kexecdh_reply(self, m): peer_host_key_bytes = m.get_string() peer_key_bytes = m.get_string() sig = m.get_binary() peer_key = X25519PublicKey.from_public_bytes(peer_key_bytes) K = self._perform_exchange(peer_key) K = long(binascii.hexlify(K), 16) # compute exchange hash and verify signature hm = Message() hm.add( self.transport.local_version, self.transport.remote_version, self.transport.local_kex_init, self.transport.remote_kex_init, ) hm.add_string(peer_host_key_bytes) hm.add_string(self.key.public_key().public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw)) hm.add_string(peer_key_bytes) hm.add_mpint(K) self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest()) self.transport._verify_key(peer_host_key_bytes, sig) self.transport._activate_outbound()
def _parse_kexecdh_reply(self, m): K_S = m.get_string() Q_S_bytes = m.get_string() self.Q_S = ec.EllipticCurvePublicKey.from_encoded_point( self.curve, Q_S_bytes ) sig = m.get_binary() K = self.P.exchange(ec.ECDH(), self.Q_S) K = long(hexlify(K), 16) # compute exchange hash and verify signature hm = Message() hm.add( self.transport.local_version, self.transport.remote_version, self.transport.local_kex_init, self.transport.remote_kex_init, ) hm.add_string(K_S) # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion hm.add_string( self.Q_C.public_bytes( serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint, ) ) hm.add_string(Q_S_bytes) hm.add_mpint(K) self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest()) self.transport._verify_key(K_S, sig) self.transport._activate_outbound()
def _parse_kexgss_complete(self, m): """ Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode). :param `.Message` m: The content of the SSH2_MSG_KEXGSS_COMPLETE message """ # client mode if self.transport.host_key is None: self.transport.host_key = NullHostKey() self.f = m.get_mpint() if (self.f < 1) or (self.f > self.P - 1): raise SSHException('Server kex "f" is out of range') mic_token = m.get_string() # This must be TRUE, if there is a GSS-API token in this message. bool = m.get_boolean() srv_token = None if bool: srv_token = m.get_string() K = pow(self.f, self.x, self.P) # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K) hm = Message() hm.add(self.transport.local_version, self.transport.remote_version, self.transport.local_kex_init, self.transport.remote_kex_init) hm.add_string(self.transport.host_key.__str__()) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) self.transport._set_K_H(K, sha1(str(hm)).digest()) if srv_token is not None: self.kexgss.ssh_init_sec_context(target=self.gss_host, recv_token=srv_token) self.kexgss.ssh_check_mic(mic_token, self.transport.session_id) else: self.kexgss.ssh_check_mic(mic_token, self.transport.session_id) self.transport._activate_outbound()
def generate_key_pair(request): """ Response to generate private/public RSA key pair """ get_user(request, settings.ASTAKOS_AUTH_URL) if request.method != "POST": return http.HttpResponseNotAllowed(["POST"]) if not SUPPORT_GENERATE_KEYS: raise Exception("Application does not support ssh keys generation") if PublicKeyPair.user_limit_exceeded(request.user_uniq): return http.HttpResponseServerError("SSH keys limit exceeded") # generate RSA key from Crypto import Random Random.atfork() key = rsakey.RSA.generate(SSH_KEY_LENGTH) # get PEM string pem = exportKey(key, 'PEM') public_data = Message() public_data.add_string('ssh-rsa') public_data.add_mpint(key.key.e) public_data.add_mpint(key.key.n) # generate public content public = str("ssh-rsa %s" % base64.b64encode(str(public_data))) data = {'private': pem, 'public': public} return http.HttpResponse(json.dumps(data), mimetype="application/json")
def _parse_kexecdh_init(self, m): Q_C_bytes = m.get_string() self.Q_C = ec.EllipticCurvePublicNumbers.from_encoded_point( self.curve, Q_C_bytes ) K_S = self.transport.get_server_key().asbytes() K = self.P.exchange(ec.ECDH(), self.Q_C.public_key(default_backend())) K = long(hexlify(K), 16) # compute exchange hash hm = Message() hm.add(self.transport.remote_version, self.transport.local_version, self.transport.remote_kex_init, self.transport.local_kex_init) hm.add_string(K_S) hm.add_string(Q_C_bytes) # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion hm.add_string(self.Q_S.public_numbers().encode_point()) hm.add_mpint(long(K)) H = self.hash_algo(hm.asbytes()).digest() self.transport._set_K_H(K, H) sig = self.transport.get_server_key().sign_ssh_data(H) # construct reply m = Message() m.add_byte(c_MSG_KEXECDH_REPLY) m.add_string(K_S) m.add_string(self.Q_S.public_numbers().encode_point()) m.add_string(sig) self.transport._send_message(m) self.transport._activate_outbound()
def make_certificate(ca_key, duration_hours, real_name, username, host, now, expiry): """http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=1.9""" pkey = paramiko.RSAKey.from_private_key(StringIO(ca_key)) principals = Message() principals.add_string(username) principals = principals.asbytes() m = Message() m.add_string('*****@*****.**') m.add_string(sha1(str(random.random())).hexdigest()) m.add_mpint(pkey.e) m.add_mpint(pkey.n) m.add_int64(0) # serial m.add_int(SSH_CERT_TYPE_USER) m.add_string(real_name) m.add_string(principals) m.add_int64(int(now.strftime("%s"))) m.add_int64(int(expiry.strftime("%s"))) m.add_string("") # critical_options m.add_string("") # extensions m.add_string("") # reserved m.add_string(pkey.asbytes()) key = RSA.construct((long(pkey.n), long(pkey.e), long(pkey.d))) h = SHA.new(m.asbytes()) signer = PKCS1_v1_5.new(key) signature = signer.sign(h) sig_message = Message() sig_message.add_string("ssh-rsa") sig_message.add_string(signature) m.add_string(sig_message.asbytes()) return "[email protected] {0} {1}@{2}".format(base64.b64encode(m.asbytes()), username, host)
def _parse_kexdh_gex_request_old(self, m): # same as above, but without min_bits or max_bits (used by older # clients like putty) self.preferred_bits = m.get_int() # smoosh the user's preferred size into our own limits if self.preferred_bits > self.max_bits: self.preferred_bits = self.max_bits if self.preferred_bits < self.min_bits: self.preferred_bits = self.min_bits # generate prime pack = self.transport._get_modulus_pack() if pack is None: raise SSHException("Can't do server-side gex with no modulus pack") self.transport._log( DEBUG, "Picking p (~ {} bits)".format(self.preferred_bits) ) self.g, self.p = pack.get_modulus( self.min_bits, self.preferred_bits, self.max_bits ) m = Message() m.add_byte(c_MSG_KEXDH_GEX_GROUP) m.add_mpint(self.p) m.add_mpint(self.g) self.transport._send_message(m) self.transport._expect_packet(_MSG_KEXDH_GEX_INIT) self.old_style = True
def start_kex(self): """ Start the GSS-API / SSPI Authenticated Diffie-Hellman Key Exchange. """ self._generate_x() if self.transport.server_mode: # compute f = g^x mod p, but don't send it yet self.f = pow(self.G, self.x, self.P) self.transport._expect_packet(MSG_KEXGSS_INIT) return # compute e = g^x mod p (where g=2), and send it self.e = pow(self.G, self.x, self.P) # Initialize GSS-API Key Exchange self.gss_host = self.transport.gss_host m = Message() m.add_byte(c_MSG_KEXGSS_INIT) m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host)) m.add_mpint(self.e) self.transport._send_message(m) self.transport._expect_packet( MSG_KEXGSS_HOSTKEY, MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR, )
def _parse_kexgss_group(self, m): """ Parse the SSH2_MSG_KEXGSS_GROUP message (client mode). :param `Message` m: The content of the SSH2_MSG_KEXGSS_GROUP message """ self.p = m.get_mpint() self.g = m.get_mpint() # reject if p's bit length < 1024 or > 8192 bitlen = paramiko.util.bit_length(self.p) if (bitlen < 1024) or (bitlen > 8192): raise SSHException( 'Server-generated gex p (don\'t ask) is out of range ' '(%d bits)' % bitlen) self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen) # noqa self._generate_x() # now compute e = g^x mod p self.e = pow(self.g, self.x, self.p) m = Message() m.add_byte(c_MSG_KEXGSS_INIT) m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host)) m.add_mpint(self.e) self.transport._send_message(m) self.transport._expect_packet(MSG_KEXGSS_HOSTKEY, MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR)
def _parse_kexgss_group(self, m): """ Parse the SSH2_MSG_KEXGSS_GROUP message (client mode). :param `Message` m: The content of the SSH2_MSG_KEXGSS_GROUP message """ self.p = m.get_mpint() self.g = m.get_mpint() # reject if p's bit length < 1024 or > 8192 bitlen = util.bit_length(self.p) if (bitlen < 1024) or (bitlen > 8192): raise SSHException( 'Server-generated gex p (don\'t ask) is out of range ' '({} bits)'.format(bitlen)) self.transport._log(DEBUG, 'Got server p ({} bits)'.format(bitlen)) # noqa self._generate_x() # now compute e = g^x mod p self.e = pow(self.g, self.x, self.p) m = Message() m.add_byte(c_MSG_KEXGSS_INIT) m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host)) m.add_mpint(self.e) self.transport._send_message(m) self.transport._expect_packet(MSG_KEXGSS_HOSTKEY, MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR)
def _parse_kexecdh_init(self, m): peer_key_bytes = m.get_string() peer_key = X25519PublicKey.from_public_bytes(peer_key_bytes) K = self._perform_exchange(peer_key) K = long(binascii.hexlify(K), 16) # compute exchange hash hm = Message() hm.add( self.transport.remote_version, self.transport.local_version, self.transport.remote_kex_init, self.transport.local_kex_init, ) server_key_bytes = self.transport.get_server_key().asbytes() exchange_key_bytes = self.key.public_key().public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw) hm.add_string(server_key_bytes) hm.add_string(peer_key_bytes) hm.add_string(exchange_key_bytes) hm.add_mpint(K) H = self.hash_algo(hm.asbytes()).digest() self.transport._set_K_H(K, H) sig = self.transport.get_server_key().sign_ssh_data(H) # construct reply m = Message() m.add_byte(c_MSG_KEXECDH_REPLY) m.add_string(server_key_bytes) m.add_string(exchange_key_bytes) m.add_string(sig) self.transport._send_message(m) self.transport._activate_outbound()
def _parse_kexdh_gex_request(self, m): minbits = m.get_int() preferredbits = m.get_int() maxbits = m.get_int() # smoosh the user's preferred size into our own limits if preferredbits > self.max_bits: preferredbits = self.max_bits if preferredbits < self.min_bits: preferredbits = self.min_bits # fix min/max if they're inconsistent. technically, we could just pout # and hang up, but there's no harm in giving them the benefit of the # doubt and just picking a bitsize for them. if minbits > preferredbits: minbits = preferredbits if maxbits < preferredbits: maxbits = preferredbits # now save a copy self.min_bits = minbits self.preferred_bits = preferredbits self.max_bits = maxbits # generate prime pack = self.transport._get_modulus_pack() if pack is None: raise SSHException('Can\'t do server-side gex with no modulus pack') self.transport._log(DEBUG, 'Picking p (%d <= %d <= %d bits)' % (minbits, preferredbits, maxbits)) self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits) m = Message() m.add_byte(chr(_MSG_KEXDH_GEX_GROUP)) m.add_mpint(self.p) m.add_mpint(self.g) self.transport._send_message(m) self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
def __bytes__(self): m = Message() m.add_string(b'ssh-dss') m.add_mpint(self.p) m.add_mpint(self.q) m.add_mpint(self.g) m.add_mpint(self.y) return bytes(m)
def asbytes(self): m = Message() m.add_string('ssh-dss') m.add_mpint(self.p) m.add_mpint(self.q) m.add_mpint(self.g) m.add_mpint(self.y) return m.asbytes()
def __str__(self): m = Message() m.add_string("ssh-dss") m.add_mpint(self.p) m.add_mpint(self.q) m.add_mpint(self.g) m.add_mpint(self.y) return str(m)
def __str__(self): m = Message() m.add_string('ssh-dss') m.add_mpint(self.p) m.add_mpint(self.q) m.add_mpint(self.g) m.add_mpint(self.y) return str(m)
def _parse_dsa_key(msg): # See comment for _parse_rsa_key key_msg = Message() key_msg.add_string('ssh-dss') key_msg.add_mpint(msg.get_mpint()) key_msg.add_mpint(msg.get_mpint()) key_msg.add_mpint(msg.get_mpint()) key_msg.add_mpint(msg.get_mpint()) key_msg.rewind() return DSSKey(msg=key_msg)
def _parse_rsa_key(msg): # This method avoids having to keep track of how paramiko handles keys # or having to go into the cryptography.hazmat modules. It takes the # parts of the key from the certificate then puts them in a Message that # resembles a normally encoded keyh. That Message is then handed to the # key constructor which should be able to handle the parsing. key_msg = Message() key_msg.add_string('ssh-rsa') key_msg.add_mpint(msg.get_mpint()) key_msg.add_mpint(msg.get_mpint()) key_msg.rewind() return RSAKey(msg=key_msg)
def _parse_kexdh_gex_init(self, m): self.e = m.get_mpint() if (self.e < 1) or (self.e > self.p - 1): raise SSHException('Client kex "e" is out of range') self._generate_x() self.f = pow(self.g, self.x, self.p) K = pow(self.e, self.x, self.p) key = str(self.transport.get_server_key()) # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) hm = Message() hm.add(self.transport.remote_version, self.transport.local_version, self.transport.remote_kex_init, self.transport.local_kex_init, key) if not self.old_style: hm.add_int(self.min_bits) hm.add_int(self.preferred_bits) if not self.old_style: hm.add_int(self.max_bits) hm.add_mpint(self.p) hm.add_mpint(self.g) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) H = SHA.new(str(hm)).digest() self.transport._set_K_H(K, H) # sign it sig = self.transport.get_server_key().sign_ssh_data(self.transport.randpool, H) # send reply m = Message() m.add_byte(chr(_MSG_KEXDH_GEX_REPLY)) m.add_string(key) m.add_mpint(self.f) m.add_string(str(sig)) self.transport._send_message(m) self.transport._activate_outbound()
def start_kex(self): self._generate_x() if self.transport.server_mode: # compute f = g^x mod p, but don't send it yet self.f = pow(G, self.x, P) self.transport._expect_packet(_MSG_KEXDH_INIT) return # compute e = g^x mod p (where g=2), and send it self.e = pow(G, self.x, P) m = Message() m.add_byte(chr(_MSG_KEXDH_INIT)) m.add_mpint(self.e) self.transport._send_message(m) self.transport._expect_packet(_MSG_KEXDH_REPLY)
def _parse_kexgss_gex_init(self, m): """ Parse the SSH2_MSG_KEXGSS_INIT message (server mode). :param `Message` m: The content of the SSH2_MSG_KEXGSS_INIT message """ client_token = m.get_string() self.e = m.get_mpint() if (self.e < 1) or (self.e > self.p - 1): raise SSHException('Client kex "e" is out of range') self._generate_x() self.f = pow(self.g, self.x, self.p) K = pow(self.e, self.x, self.p) self.transport.host_key = NullHostKey() key = self.transport.host_key.__str__() # okay, build up the hash H of # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa hm = Message() hm.add( self.transport.remote_version, self.transport.local_version, self.transport.remote_kex_init, self.transport.local_kex_init, key, ) hm.add_int(self.min_bits) hm.add_int(self.preferred_bits) hm.add_int(self.max_bits) hm.add_mpint(self.p) hm.add_mpint(self.g) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) H = sha1(hm.asbytes()).digest() self.transport._set_K_H(K, H) srv_token = self.kexgss.ssh_accept_sec_context(self.gss_host, client_token) m = Message() if self.kexgss._gss_srv_ctxt_status: mic_token = self.kexgss.ssh_get_mic(self.transport.session_id, gss_kex=True) m.add_byte(c_MSG_KEXGSS_COMPLETE) m.add_mpint(self.f) m.add_string(mic_token) if srv_token is not None: m.add_boolean(True) m.add_string(srv_token) else: m.add_boolean(False) self.transport._send_message(m) self.transport.gss_kex_used = True self.transport._activate_outbound() else: m.add_byte(c_MSG_KEXGSS_CONTINUE) m.add_string(srv_token) self.transport._send_message(m) self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR)
def _parse_kexdh_gex_group(self, m): self.p = m.get_mpint() self.g = m.get_mpint() # reject if p's bit length < 1024 or > 8192 bitlen = util.bit_length(self.p) if (bitlen < 1024) or (bitlen > 8192): raise SSHException('Server-generated gex p (don\'t ask) is out of range (%d bits)' % bitlen) self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen) self._generate_x() # now compute e = g^x mod p self.e = pow(self.g, self.x, self.p) m = Message() m.add_byte(chr(_MSG_KEXDH_GEX_INIT)) m.add_mpint(self.e) self.transport._send_message(m) self.transport._expect_packet(_MSG_KEXDH_GEX_REPLY)
def add_key_cert(self, key, cert): if not key.can_sign(): err = "Key cannot be used for signing or added to the agent {}" raise SSHException(err) if cert.valid_before < certificate.MAX_CERT_VALID_BEFORE: req_type = SSH2_AGENTC_ADD_ID_CONSTRAINED time_left = int(cert.valid_before - time.time()) else: req_type = SSH2_AGENTC_ADD_IDENTITY time_left = None msg = Message() msg.add_byte(req_type) msg.add_string(cert.get_name()) msg.add_string(cert.asbytes()) if type(key) == RSAKey: # Convert private key to a cryptography key class. This is to get # around the variations in how paramiko handles keys internally buf = StringIO() key.write_private_key(buf) buf.seek(0) backend = default_backend() new_key = serialization.load_pem_private_key(str(buf.read()), password=None, backend=backend) priv_numbers = new_key.private_numbers() msg.add_mpint(priv_numbers.d) msg.add_mpint(priv_numbers.iqmp) msg.add_mpint(priv_numbers.p) msg.add_mpint(priv_numbers.q) elif type(key) == DSSKey: msg.add_mpint(key.x) elif type(key) == ECDSAKey: msg.add_mpint(key.signing_key.privkey.secret_multiplier) msg.add_string(cert.key_id) if time_left: msg.add_byte(SSH_AGENT_CONSTRAIN_LIFETIME) msg.add_int(time_left) restype, res = self._send_message(msg) if restype != SSH_AGENT_SUCCESS: return False return True
def _message_without_signature(self): m = Message() m.add_string('*****@*****.**') m.add_string(self.nonce) m.add_mpint(self.public_numbers.e) m.add_mpint(self.public_numbers.n) m.add_int64(self.serial) m.add_int(self.type) m.add_string(self.key_id) m.add_string(self.valid_principals) m.add_int64(self.valid_after) m.add_int64(self.valid_before) m.add_string(self.critical_options) m.add_string(self.extensions) m.add_string(self.reserved) m.add_string(self.signature_key) return m
def asagentbytes(self): m = Message() m.add_string('ssh-rsa') m.add_mpint(self.public_numbers.n) m.add_mpint(self.public_numbers.e) m.add_mpint(self.key.private_numbers().d) m.add_mpint(self.key.private_numbers().iqmp) m.add_mpint(self.key.private_numbers().p) m.add_mpint(self.key.private_numbers().q) return m.asbytes()
def _parse_kexgss_gex_init(self, m): """ Parse the SSH2_MSG_KEXGSS_INIT message (server mode). :param `Message` m: The content of the SSH2_MSG_KEXGSS_INIT message """ client_token = m.get_string() self.e = m.get_mpint() if (self.e < 1) or (self.e > self.p - 1): raise SSHException('Client kex "e" is out of range') self._generate_x() self.f = pow(self.g, self.x, self.p) K = pow(self.e, self.x, self.p) self.transport.host_key = NullHostKey() key = self.transport.host_key.__str__() # okay, build up the hash H of # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa hm = Message() hm.add(self.transport.remote_version, self.transport.local_version, self.transport.remote_kex_init, self.transport.local_kex_init, key) hm.add_int(self.min_bits) hm.add_int(self.preferred_bits) hm.add_int(self.max_bits) hm.add_mpint(self.p) hm.add_mpint(self.g) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) H = sha1(hm.asbytes()).digest() self.transport._set_K_H(K, H) srv_token = self.kexgss.ssh_accept_sec_context(self.gss_host, client_token) m = Message() if self.kexgss._gss_srv_ctxt_status: mic_token = self.kexgss.ssh_get_mic(self.transport.session_id, gss_kex=True) m.add_byte(c_MSG_KEXGSS_COMPLETE) m.add_mpint(self.f) m.add_string(mic_token) if srv_token is not None: m.add_boolean(True) m.add_string(srv_token) else: m.add_boolean(False) self.transport._send_message(m) self.transport.gss_kex_used = True self.transport._activate_outbound() else: m.add_byte(c_MSG_KEXGSS_CONTINUE) m.add_string(srv_token) self.transport._send_message(m) self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR)
def _parse_kexgss_groupreq(self, m): """ Parse the SSH2_MSG_KEXGSS_GROUPREQ message (server mode). :param `.Message` m: The content of the SSH2_MSG_KEXGSS_GROUPREQ message """ minbits = m.get_int() preferredbits = m.get_int() maxbits = m.get_int() # smoosh the user's preferred size into our own limits if preferredbits > self.max_bits: preferredbits = self.max_bits if preferredbits < self.min_bits: preferredbits = self.min_bits # fix min/max if they're inconsistent. technically, we could just pout # and hang up, but there's no harm in giving them the benefit of the # doubt and just picking a bitsize for them. if minbits > preferredbits: minbits = preferredbits if maxbits < preferredbits: maxbits = preferredbits # now save a copy self.min_bits = minbits self.preferred_bits = preferredbits self.max_bits = maxbits # generate prime pack = self.transport._get_modulus_pack() if pack is None: raise SSHException( 'Can\'t do server-side gex with no modulus pack') self.transport._log( DEBUG, # noqa 'Picking p ({} <= {} <= {} bits)'.format( minbits, preferredbits, maxbits, )) self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits) m = Message() m.add_byte(c_MSG_KEXGSS_GROUP) m.add_mpint(self.p) m.add_mpint(self.g) self.transport._send_message(m) self.transport._expect_packet(MSG_KEXGSS_INIT)
def _parse_kexgss_groupreq(self, m): """ Parse the SSH2_MSG_KEXGSS_GROUPREQ message (server mode). :param `.Message` m: The content of the SSH2_MSG_KEXGSS_GROUPREQ message """ minbits = m.get_int() preferredbits = m.get_int() maxbits = m.get_int() # smoosh the user's preferred size into our own limits if preferredbits > self.max_bits: preferredbits = self.max_bits if preferredbits < self.min_bits: preferredbits = self.min_bits # fix min/max if they're inconsistent. technically, we could just pout # and hang up, but there's no harm in giving them the benefit of the # doubt and just picking a bitsize for them. if minbits > preferredbits: minbits = preferredbits if maxbits < preferredbits: maxbits = preferredbits # now save a copy self.min_bits = minbits self.preferred_bits = preferredbits self.max_bits = maxbits # generate prime pack = self.transport._get_modulus_pack() if pack is None: raise SSHException( 'Can\'t do server-side gex with no modulus pack') self.transport._log( DEBUG, # noqa 'Picking p ({} <= {} <= {} bits)'.format( minbits, preferredbits, maxbits, ) ) self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits) m = Message() m.add_byte(c_MSG_KEXGSS_GROUP) m.add_mpint(self.p) m.add_mpint(self.g) self.transport._send_message(m) self.transport._expect_packet(MSG_KEXGSS_INIT)
def _parse_kexgss_complete(self, m): """ Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode). :param `.Message` m: The content of the SSH2_MSG_KEXGSS_COMPLETE message """ # client mode if self.transport.host_key is None: self.transport.host_key = NullHostKey() self.f = m.get_mpint() if (self.f < 1) or (self.f > self.P - 1): raise SSHException('Server kex "f" is out of range') mic_token = m.get_string() # This must be TRUE, if there is a GSS-API token in this message. bool = m.get_boolean() srv_token = None if bool: srv_token = m.get_string() K = pow(self.f, self.x, self.P) # okay, build up the hash H of # (V_C || V_S || I_C || I_S || K_S || e || f || K) hm = Message() hm.add( self.transport.local_version, self.transport.remote_version, self.transport.local_kex_init, self.transport.remote_kex_init, ) hm.add_string(self.transport.host_key.__str__()) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) H = sha1(str(hm)).digest() self.transport._set_K_H(K, H) if srv_token is not None: self.kexgss.ssh_init_sec_context( target=self.gss_host, recv_token=srv_token ) self.kexgss.ssh_check_mic(mic_token, H) else: self.kexgss.ssh_check_mic(mic_token, H) self.transport.gss_kex_used = True self.transport._activate_outbound()
def _parse_kexc25519_init(self, m): # server mode # Only one field in the client's message, which is their public key Q_C_bytes = m.get_string() self.Q_C = x25519.X25519PublicKey.from_public_bytes(Q_C_bytes) # Compute shared secret K = self.P.exchange(self.Q_C) K = long(hexlify(K), 16) # Prepare hostkey K_S = self.transport.get_server_key().asbytes() # Compute initial transport key hm = Message() hm.add( self.transport.remote_version, self.transport.local_version, self.transport.remote_kex_init, self.transport.local_kex_init, ) hm.add_string(K_S) hm.add_string(Q_C_bytes) hm.add_string( self.Q_S.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw)) hm.add_mpint(K) H = self.hash_algo(hm.asbytes()).digest() self.transport._set_K_H(K, H) # Compute signature sig = self.transport.get_server_key().sign_ssh_data(H) # construct reply m = Message() m.add_byte(c_MSG_KEXC25519_REPLY) m.add_string(K_S) m.add_string( self.Q_S.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw)) m.add_string(sig) self.transport._send_message(m) self.transport._activate_outbound()
def _parse_kexdh_reply(self, m): # client mode host_key = m.get_string() self.f = m.get_mpint() if (self.f < 1) or (self.f > P - 1): raise SSHException('Server kex "f" is out of range') sig = m.get_string() K = pow(self.f, self.x, P) # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K) hm = Message() hm.add(self.transport.local_version, self.transport.remote_version, self.transport.local_kex_init, self.transport.remote_kex_init) hm.add_string(host_key) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) self.transport._set_K_H(K, SHA.new(str(hm)).digest()) self.transport._verify_key(host_key, sig) self.transport._activate_outbound()
def generate_keypair(): # generate RSA key from Crypto import Random Random.atfork() key = rsakey.RSA.generate(SSH_KEY_LENGTH) # get PEM string pem = exportKey(key, 'PEM') public_data = Message() public_data.add_string('ssh-rsa') public_data.add_mpint(key.key.e) public_data.add_mpint(key.key.n) # generate public content public = str("ssh-rsa %s" % base64.b64encode(str(public_data))) data = {'private': pem, 'public': public} return data
def _parse_kexdh_init(self, m): # server mode self.e = m.get_mpint() if (self.e < 1) or (self.e > P - 1): raise SSHException('Client kex "e" is out of range') K = pow(self.e, self.x, P) key = str(self.transport.get_server_key()) # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K) hm = Message() hm.add(self.transport.remote_version, self.transport.local_version, self.transport.remote_kex_init, self.transport.local_kex_init) hm.add_string(key) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) H = SHA.new(str(hm)).digest() self.transport._set_K_H(K, H) # sign it sig = self.transport.get_server_key().sign_ssh_data(self.transport.randpool, H) # send reply m = Message() m.add_byte(chr(_MSG_KEXDH_REPLY)) m.add_string(key) m.add_mpint(self.f) m.add_string(str(sig)) self.transport._send_message(m) self.transport._activate_outbound()
def _parse_kexecdh_init(self, m): Q_C_bytes = m.get_string() self.Q_C = ec.EllipticCurvePublicKey.from_encoded_point( self.curve, Q_C_bytes) K_S = self.transport.get_server_key().asbytes() K = self.P.exchange(ec.ECDH(), self.Q_C) K = long(hexlify(K), 16) # compute exchange hash hm = Message() hm.add( self.transport.remote_version, self.transport.local_version, self.transport.remote_kex_init, self.transport.local_kex_init, ) hm.add_string(K_S) hm.add_string(Q_C_bytes) # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion hm.add_string( self.Q_S.public_bytes( serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint, )) hm.add_mpint(long(K)) H = self.hash_algo(hm.asbytes()).digest() self.transport._set_K_H(K, H) sig = self.transport.get_server_key().sign_ssh_data( H, self.transport.host_key_type) # construct reply m = Message() m.add_byte(c_MSG_KEXECDH_REPLY) m.add_string(K_S) m.add_string( self.Q_S.public_bytes( serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint, )) m.add_string(sig) self.transport._send_message(m) self.transport._activate_outbound()
def _parse_kexdh_gex_reply(self, m): host_key = m.get_string() self.f = m.get_mpint() sig = m.get_string() if (self.f < 1) or (self.f > self.p - 1): raise SSHException('Server kex "f" is out of range') K = pow(self.f, self.x, self.p) # okay, build up the hash H of # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa hm = Message() hm.add( self.transport.local_version, self.transport.remote_version, self.transport.local_kex_init, self.transport.remote_kex_init, host_key, ) if not self.old_style: hm.add_int(self.min_bits) hm.add_int(self.preferred_bits) if not self.old_style: hm.add_int(self.max_bits) hm.add_mpint(self.p) hm.add_mpint(self.g) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest()) self.transport._verify_key(host_key, sig) self.transport._activate_outbound()
def _parse_kexdh_gex_reply(self, m): host_key = m.get_string() self.f = m.get_mpint() sig = m.get_string() if (self.f < 1) or (self.f > self.p - 1): raise SSHException('Server kex "f" is out of range') K = pow(self.f, self.x, self.p) # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) hm = Message() hm.add(self.transport.local_version, self.transport.remote_version, self.transport.local_kex_init, self.transport.remote_kex_init, host_key) if not self.old_style: hm.add_int(self.min_bits) hm.add_int(self.preferred_bits) if not self.old_style: hm.add_int(self.max_bits) hm.add_mpint(self.p) hm.add_mpint(self.g) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest()) self.transport._verify_key(host_key, sig) self.transport._activate_outbound()
def test_encode(self): msg = Message() msg.add_int(23) msg.add_int(123789456) msg.add_string('q') msg.add_string('hello') msg.add_string('x' * 1000) self.assertEqual(msg.asbytes(), self.__a) msg = Message() msg.add_boolean(True) msg.add_boolean(False) msg.add_byte(byte_chr(0xf3)) msg.add_bytes(zero_byte + byte_chr(0x3f)) msg.add_list(['huey', 'dewey', 'louie']) self.assertEqual(msg.asbytes(), self.__b) msg = Message() msg.add_int64(5) msg.add_int64(0xf5e4d3c2b109) msg.add_mpint(17) msg.add_mpint(0xf5e4d3c2b109) msg.add_mpint(-0x65e4d3c2b109) self.assertEqual(msg.asbytes(), self.__c)
def test_1_encode(self): msg = Message() msg.add_int(23) msg.add_int(123789456) msg.add_string("q") msg.add_string("hello") msg.add_string("x" * 1000) self.assertEqual(msg.asbytes(), self.__a) msg = Message() msg.add_boolean(True) msg.add_boolean(False) msg.add_byte(byte_chr(0xf3)) msg.add_bytes(zero_byte + byte_chr(0x3f)) msg.add_list(["huey", "dewey", "louie"]) self.assertEqual(msg.asbytes(), self.__b) msg = Message() msg.add_int64(5) msg.add_int64(0xf5e4d3c2b109) msg.add_mpint(17) msg.add_mpint(0xf5e4d3c2b109) msg.add_mpint(-0x65e4d3c2b109) self.assertEqual(msg.asbytes(), self.__c)
def _parse_kexgss_complete(self, m): """ Parse the SSH2_MSG_KEXGSS_COMPLETE message (client mode). :param `Message` m: The content of the SSH2_MSG_KEXGSS_COMPLETE message """ if self.transport.host_key is None: self.transport.host_key = NullHostKey() self.f = m.get_mpint() mic_token = m.get_string() # This must be TRUE, if there is a GSS-API token in this message. bool = m.get_boolean() srv_token = None if bool: srv_token = m.get_string() if (self.f < 1) or (self.f > self.p - 1): raise SSHException('Server kex "f" is out of range') K = pow(self.f, self.x, self.p) # okay, build up the hash H of # (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa hm = Message() hm.add(self.transport.local_version, self.transport.remote_version, self.transport.local_kex_init, self.transport.remote_kex_init, self.transport.host_key.__str__()) if not self.old_style: hm.add_int(self.min_bits) hm.add_int(self.preferred_bits) if not self.old_style: hm.add_int(self.max_bits) hm.add_mpint(self.p) hm.add_mpint(self.g) hm.add_mpint(self.e) hm.add_mpint(self.f) hm.add_mpint(K) H = sha1(hm.asbytes()).digest() self.transport._set_K_H(K, H) if srv_token is not None: self.kexgss.ssh_init_sec_context(target=self.gss_host, recv_token=srv_token) self.kexgss.ssh_check_mic(mic_token, self.transport.session_id) else: self.kexgss.ssh_check_mic(mic_token, self.transport.session_id) self.transport._activate_outbound()
def test_1_encode(self): msg = Message() msg.add_int(23) msg.add_int(123789456) msg.add_string("q") msg.add_string("hello") msg.add_string("x" * 1000) self.assertEquals(str(msg), self.__a) msg = Message() msg.add_boolean(True) msg.add_boolean(False) msg.add_byte("\xf3") msg.add_bytes("\x00\x3f") msg.add_list(["huey", "dewey", "louie"]) self.assertEquals(str(msg), self.__b) msg = Message() msg.add_int64(5) msg.add_int64(0xF5E4D3C2B109L) msg.add_mpint(17) msg.add_mpint(0xF5E4D3C2B109L) msg.add_mpint(-0x65E4D3C2B109L) self.assertEquals(str(msg), self.__c)
def test_1_encode(self): msg = Message() msg.add_int(23) msg.add_int(123789456) msg.add_string(b'q') msg.add_string(b'hello') msg.add_string(b'x' * 1000) self.assertEquals(bytes(msg), self.__a) msg = Message() msg.add_boolean(True) msg.add_boolean(False) msg.add_byte(b'\xf3') msg.add_bytes(b'\x00\x3f') msg.add_list([b'huey', b'dewey', b'louie']) self.assertEquals(bytes(msg), self.__b) msg = Message() msg.add_int64(5) msg.add_int64(0xf5e4d3c2b109) msg.add_mpint(17) msg.add_mpint(0xf5e4d3c2b109) msg.add_mpint(-0x65e4d3c2b109) self.assertEquals(bytes(msg), self.__c)
def asbytes(self): m = Message() m.add_string('ssh-rsa') m.add_mpint(self.e) m.add_mpint(self.n) return m.asbytes()
def __bytes__(self): m = Message() m.add_string(b'ssh-rsa') m.add_mpint(self.e) m.add_mpint(self.n) return bytes(m)
def _sigencode(self, r, s, order): msg = Message() msg.add_mpint(r) msg.add_mpint(s) return str(msg)
def __str__(self): m = Message() m.add_string('ssh-rsa') m.add_mpint(self.e) m.add_mpint(self.n) return str(m)
def _sigencode(self, r, s, order): msg = Message() msg.add_mpint(r) msg.add_mpint(s) return msg.asbytes()