Home | Trees | Indices | Help |
---|
|
1 # Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> 2 # 3 # This file is part of paramiko. 4 # 5 # Paramiko is free software; you can redistribute it and/or modify it under the 6 # terms of the GNU Lesser General Public License as published by the Free 7 # Software Foundation; either version 2.1 of the License, or (at your option) 8 # any later version. 9 # 10 # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 # details. 14 # 15 # You should have received a copy of the GNU Lesser General Public License 16 # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 19 """ 20 Variant on `KexGroup1 <paramiko.kex_group1.KexGroup1>` where the prime "p" and 21 generator "g" are provided by the server. A bit more work is required on the 22 client side, and a B{lot} more on the server side. 23 """ 24 25 import os 26 from hashlib import sha1 27 28 from paramiko import util 29 from paramiko.common import DEBUG 30 from paramiko.message import Message 31 from paramiko.py3compat import byte_chr, byte_ord, byte_mask 32 from paramiko.ssh_exception import SSHException 33 34 35 _MSG_KEXDH_GEX_REQUEST_OLD, _MSG_KEXDH_GEX_GROUP, _MSG_KEXDH_GEX_INIT, \ 36 _MSG_KEXDH_GEX_REPLY, _MSG_KEXDH_GEX_REQUEST = range(30, 35) 37 c_MSG_KEXDH_GEX_REQUEST_OLD, c_MSG_KEXDH_GEX_GROUP, c_MSG_KEXDH_GEX_INIT, \ 38 c_MSG_KEXDH_GEX_REPLY, c_MSG_KEXDH_GEX_REQUEST = [byte_chr(c) for c in range(30, 35)] 39 4042 43 name = 'diffie-hellman-group-exchange-sha1' 44 min_bits = 1024 45 max_bits = 8192 46 preferred_bits = 2048 4724549 self.transport = transport 50 self.p = None 51 self.q = None 52 self.g = None 53 self.x = None 54 self.e = None 55 self.f = None 56 self.old_style = False5759 if self.transport.server_mode: 60 self.transport._expect_packet(_MSG_KEXDH_GEX_REQUEST, _MSG_KEXDH_GEX_REQUEST_OLD) 61 return 62 # request a bit range: we accept (min_bits) to (max_bits), but prefer 63 # (preferred_bits). according to the spec, we shouldn't pull the 64 # minimum up above 1024. 65 m = Message() 66 if _test_old_style: 67 # only used for unit tests: we shouldn't ever send this 68 m.add_byte(c_MSG_KEXDH_GEX_REQUEST_OLD) 69 m.add_int(self.preferred_bits) 70 self.old_style = True 71 else: 72 m.add_byte(c_MSG_KEXDH_GEX_REQUEST) 73 m.add_int(self.min_bits) 74 m.add_int(self.preferred_bits) 75 m.add_int(self.max_bits) 76 self.transport._send_message(m) 77 self.transport._expect_packet(_MSG_KEXDH_GEX_GROUP)7880 if ptype == _MSG_KEXDH_GEX_REQUEST: 81 return self._parse_kexdh_gex_request(m) 82 elif ptype == _MSG_KEXDH_GEX_GROUP: 83 return self._parse_kexdh_gex_group(m) 84 elif ptype == _MSG_KEXDH_GEX_INIT: 85 return self._parse_kexdh_gex_init(m) 86 elif ptype == _MSG_KEXDH_GEX_REPLY: 87 return self._parse_kexdh_gex_reply(m) 88 elif ptype == _MSG_KEXDH_GEX_REQUEST_OLD: 89 return self._parse_kexdh_gex_request_old(m) 90 raise SSHException('KexGex asked to handle packet type %d' % ptype)91 92 ### internals... 9395 # generate an "x" (1 < x < (p-1)/2). 96 q = (self.p - 1) // 2 97 qnorm = util.deflate_long(q, 0) 98 qhbyte = byte_ord(qnorm[0]) 99 byte_count = len(qnorm) 100 qmask = 0xff 101 while not (qhbyte & 0x80): 102 qhbyte <<= 1 103 qmask >>= 1 104 while True: 105 x_bytes = os.urandom(byte_count) 106 x_bytes = byte_mask(x_bytes[0], qmask) + x_bytes[1:] 107 x = util.inflate_long(x_bytes, 1) 108 if (x > 1) and (x < q): 109 break 110 self.x = x111113 minbits = m.get_int() 114 preferredbits = m.get_int() 115 maxbits = m.get_int() 116 # smoosh the user's preferred size into our own limits 117 if preferredbits > self.max_bits: 118 preferredbits = self.max_bits 119 if preferredbits < self.min_bits: 120 preferredbits = self.min_bits 121 # fix min/max if they're inconsistent. technically, we could just pout 122 # and hang up, but there's no harm in giving them the benefit of the 123 # doubt and just picking a bitsize for them. 124 if minbits > preferredbits: 125 minbits = preferredbits 126 if maxbits < preferredbits: 127 maxbits = preferredbits 128 # now save a copy 129 self.min_bits = minbits 130 self.preferred_bits = preferredbits 131 self.max_bits = maxbits 132 # generate prime 133 pack = self.transport._get_modulus_pack() 134 if pack is None: 135 raise SSHException('Can\'t do server-side gex with no modulus pack') 136 self.transport._log(DEBUG, 'Picking p (%d <= %d <= %d bits)' % (minbits, preferredbits, maxbits)) 137 self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits) 138 m = Message() 139 m.add_byte(c_MSG_KEXDH_GEX_GROUP) 140 m.add_mpint(self.p) 141 m.add_mpint(self.g) 142 self.transport._send_message(m) 143 self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)144146 # same as above, but without min_bits or max_bits (used by older clients like putty) 147 self.preferred_bits = m.get_int() 148 # smoosh the user's preferred size into our own limits 149 if self.preferred_bits > self.max_bits: 150 self.preferred_bits = self.max_bits 151 if self.preferred_bits < self.min_bits: 152 self.preferred_bits = self.min_bits 153 # generate prime 154 pack = self.transport._get_modulus_pack() 155 if pack is None: 156 raise SSHException('Can\'t do server-side gex with no modulus pack') 157 self.transport._log(DEBUG, 'Picking p (~ %d bits)' % (self.preferred_bits,)) 158 self.g, self.p = pack.get_modulus(self.min_bits, self.preferred_bits, self.max_bits) 159 m = Message() 160 m.add_byte(c_MSG_KEXDH_GEX_GROUP) 161 m.add_mpint(self.p) 162 m.add_mpint(self.g) 163 self.transport._send_message(m) 164 self.transport._expect_packet(_MSG_KEXDH_GEX_INIT) 165 self.old_style = True166168 self.p = m.get_mpint() 169 self.g = m.get_mpint() 170 # reject if p's bit length < 1024 or > 8192 171 bitlen = util.bit_length(self.p) 172 if (bitlen < 1024) or (bitlen > 8192): 173 raise SSHException('Server-generated gex p (don\'t ask) is out of range (%d bits)' % bitlen) 174 self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen) 175 self._generate_x() 176 # now compute e = g^x mod p 177 self.e = pow(self.g, self.x, self.p) 178 m = Message() 179 m.add_byte(c_MSG_KEXDH_GEX_INIT) 180 m.add_mpint(self.e) 181 self.transport._send_message(m) 182 self.transport._expect_packet(_MSG_KEXDH_GEX_REPLY)183185 self.e = m.get_mpint() 186 if (self.e < 1) or (self.e > self.p - 1): 187 raise SSHException('Client kex "e" is out of range') 188 self._generate_x() 189 self.f = pow(self.g, self.x, self.p) 190 K = pow(self.e, self.x, self.p) 191 key = self.transport.get_server_key().asbytes() 192 # 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) 193 hm = Message() 194 hm.add(self.transport.remote_version, self.transport.local_version, 195 self.transport.remote_kex_init, self.transport.local_kex_init, 196 key) 197 if not self.old_style: 198 hm.add_int(self.min_bits) 199 hm.add_int(self.preferred_bits) 200 if not self.old_style: 201 hm.add_int(self.max_bits) 202 hm.add_mpint(self.p) 203 hm.add_mpint(self.g) 204 hm.add_mpint(self.e) 205 hm.add_mpint(self.f) 206 hm.add_mpint(K) 207 H = sha1(hm.asbytes()).digest() 208 self.transport._set_K_H(K, H) 209 # sign it 210 sig = self.transport.get_server_key().sign_ssh_data(H) 211 # send reply 212 m = Message() 213 m.add_byte(c_MSG_KEXDH_GEX_REPLY) 214 m.add_string(key) 215 m.add_mpint(self.f) 216 m.add_string(sig) 217 self.transport._send_message(m) 218 self.transport._activate_outbound()219221 host_key = m.get_string() 222 self.f = m.get_mpint() 223 sig = m.get_string() 224 if (self.f < 1) or (self.f > self.p - 1): 225 raise SSHException('Server kex "f" is out of range') 226 K = pow(self.f, self.x, self.p) 227 # 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) 228 hm = Message() 229 hm.add(self.transport.local_version, self.transport.remote_version, 230 self.transport.local_kex_init, self.transport.remote_kex_init, 231 host_key) 232 if not self.old_style: 233 hm.add_int(self.min_bits) 234 hm.add_int(self.preferred_bits) 235 if not self.old_style: 236 hm.add_int(self.max_bits) 237 hm.add_mpint(self.p) 238 hm.add_mpint(self.g) 239 hm.add_mpint(self.e) 240 hm.add_mpint(self.f) 241 hm.add_mpint(K) 242 self.transport._set_K_H(K, sha1(hm.asbytes()).digest()) 243 self.transport._verify_key(host_key, sig) 244 self.transport._activate_outbound()
Home | Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 on Fri May 30 12:33:19 2014 | http://epydoc.sourceforge.net |