| Home | Trees | Indices | Help |
|---|
|
|
1 # Copyright (C) 2003-2007 Robey Pointer <robey@lag.net>
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 distrubuted 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 L{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 from Crypto.Hash import SHA
26 from Crypto.Util import number
27
28 from paramiko.common import *
29 from paramiko import util
30 from paramiko.message import Message
31 from paramiko.ssh_exception import SSHException
32
33
34 _MSG_KEXDH_GEX_REQUEST_OLD, _MSG_KEXDH_GEX_GROUP, _MSG_KEXDH_GEX_INIT, \
35 _MSG_KEXDH_GEX_REPLY, _MSG_KEXDH_GEX_REQUEST = range(30, 35)
36
37
39
40 name = 'diffie-hellman-group-exchange-sha1'
41 min_bits = 1024
42 max_bits = 8192
43 preferred_bits = 2048
44
46 self.transport = transport
47 self.p = None
48 self.q = None
49 self.g = None
50 self.x = None
51 self.e = None
52 self.f = None
53 self.old_style = False
54
56 if self.transport.server_mode:
57 self.transport._expect_packet(_MSG_KEXDH_GEX_REQUEST, _MSG_KEXDH_GEX_REQUEST_OLD)
58 return
59 # request a bit range: we accept (min_bits) to (max_bits), but prefer
60 # (preferred_bits). according to the spec, we shouldn't pull the
61 # minimum up above 1024.
62 m = Message()
63 if _test_old_style:
64 # only used for unit tests: we shouldn't ever send this
65 m.add_byte(chr(_MSG_KEXDH_GEX_REQUEST_OLD))
66 m.add_int(self.preferred_bits)
67 self.old_style = True
68 else:
69 m.add_byte(chr(_MSG_KEXDH_GEX_REQUEST))
70 m.add_int(self.min_bits)
71 m.add_int(self.preferred_bits)
72 m.add_int(self.max_bits)
73 self.transport._send_message(m)
74 self.transport._expect_packet(_MSG_KEXDH_GEX_GROUP)
75
77 if ptype == _MSG_KEXDH_GEX_REQUEST:
78 return self._parse_kexdh_gex_request(m)
79 elif ptype == _MSG_KEXDH_GEX_GROUP:
80 return self._parse_kexdh_gex_group(m)
81 elif ptype == _MSG_KEXDH_GEX_INIT:
82 return self._parse_kexdh_gex_init(m)
83 elif ptype == _MSG_KEXDH_GEX_REPLY:
84 return self._parse_kexdh_gex_reply(m)
85 elif ptype == _MSG_KEXDH_GEX_REQUEST_OLD:
86 return self._parse_kexdh_gex_request_old(m)
87 raise SSHException('KexGex asked to handle packet type %d' % ptype)
88
89
90 ### internals...
91
92
94 # generate an "x" (1 < x < (p-1)/2).
95 q = (self.p - 1) // 2
96 qnorm = util.deflate_long(q, 0)
97 qhbyte = ord(qnorm[0])
98 bytes = len(qnorm)
99 qmask = 0xff
100 while not (qhbyte & 0x80):
101 qhbyte <<= 1
102 qmask >>= 1
103 while True:
104 self.transport.randpool.stir()
105 x_bytes = self.transport.randpool.get_bytes(bytes)
106 x_bytes = chr(ord(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 = x
111
113 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(chr(_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)
144
146 # 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(chr(_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 = True
166
168 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(chr(_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)
183
185 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 = str(self.transport.get_server_key())
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 = SHA.new(str(hm)).digest()
208 self.transport._set_K_H(K, H)
209 # sign it
210 sig = self.transport.get_server_key().sign_ssh_data(self.transport.randpool, H)
211 # send reply
212 m = Message()
213 m.add_byte(chr(_MSG_KEXDH_GEX_REPLY))
214 m.add_string(key)
215 m.add_mpint(self.f)
216 m.add_string(str(sig))
217 self.transport._send_message(m)
218 self.transport._activate_outbound()
219
221 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, SHA.new(str(hm)).digest())
243 self.transport._verify_key(host_key, sig)
244 self.transport._activate_outbound()
245
| Home | Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Sun Jul 6 18:30:38 2008 | http://epydoc.sourceforge.net |