| 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 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 x_bytes = self.transport.rng.read(bytes)
105 x_bytes = chr(ord(x_bytes[0]) & qmask) + x_bytes[1:]
106 x = util.inflate_long(x_bytes, 1)
107 if (x > 1) and (x < q):
108 break
109 self.x = x
110
112 minbits = m.get_int()
113 preferredbits = m.get_int()
114 maxbits = m.get_int()
115 # smoosh the user's preferred size into our own limits
116 if preferredbits > self.max_bits:
117 preferredbits = self.max_bits
118 if preferredbits < self.min_bits:
119 preferredbits = self.min_bits
120 # fix min/max if they're inconsistent. technically, we could just pout
121 # and hang up, but there's no harm in giving them the benefit of the
122 # doubt and just picking a bitsize for them.
123 if minbits > preferredbits:
124 minbits = preferredbits
125 if maxbits < preferredbits:
126 maxbits = preferredbits
127 # now save a copy
128 self.min_bits = minbits
129 self.preferred_bits = preferredbits
130 self.max_bits = maxbits
131 # generate prime
132 pack = self.transport._get_modulus_pack()
133 if pack is None:
134 raise SSHException('Can\'t do server-side gex with no modulus pack')
135 self.transport._log(DEBUG, 'Picking p (%d <= %d <= %d bits)' % (minbits, preferredbits, maxbits))
136 self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
137 m = Message()
138 m.add_byte(chr(_MSG_KEXDH_GEX_GROUP))
139 m.add_mpint(self.p)
140 m.add_mpint(self.g)
141 self.transport._send_message(m)
142 self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
143
145 # same as above, but without min_bits or max_bits (used by older clients like putty)
146 self.preferred_bits = m.get_int()
147 # smoosh the user's preferred size into our own limits
148 if self.preferred_bits > self.max_bits:
149 self.preferred_bits = self.max_bits
150 if self.preferred_bits < self.min_bits:
151 self.preferred_bits = self.min_bits
152 # generate prime
153 pack = self.transport._get_modulus_pack()
154 if pack is None:
155 raise SSHException('Can\'t do server-side gex with no modulus pack')
156 self.transport._log(DEBUG, 'Picking p (~ %d bits)' % (self.preferred_bits,))
157 self.g, self.p = pack.get_modulus(self.min_bits, self.preferred_bits, self.max_bits)
158 m = Message()
159 m.add_byte(chr(_MSG_KEXDH_GEX_GROUP))
160 m.add_mpint(self.p)
161 m.add_mpint(self.g)
162 self.transport._send_message(m)
163 self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
164 self.old_style = True
165
167 self.p = m.get_mpint()
168 self.g = m.get_mpint()
169 # reject if p's bit length < 1024 or > 8192
170 bitlen = util.bit_length(self.p)
171 if (bitlen < 1024) or (bitlen > 8192):
172 raise SSHException('Server-generated gex p (don\'t ask) is out of range (%d bits)' % bitlen)
173 self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen)
174 self._generate_x()
175 # now compute e = g^x mod p
176 self.e = pow(self.g, self.x, self.p)
177 m = Message()
178 m.add_byte(chr(_MSG_KEXDH_GEX_INIT))
179 m.add_mpint(self.e)
180 self.transport._send_message(m)
181 self.transport._expect_packet(_MSG_KEXDH_GEX_REPLY)
182
184 self.e = m.get_mpint()
185 if (self.e < 1) or (self.e > self.p - 1):
186 raise SSHException('Client kex "e" is out of range')
187 self._generate_x()
188 self.f = pow(self.g, self.x, self.p)
189 K = pow(self.e, self.x, self.p)
190 key = str(self.transport.get_server_key())
191 # 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)
192 hm = Message()
193 hm.add(self.transport.remote_version, self.transport.local_version,
194 self.transport.remote_kex_init, self.transport.local_kex_init,
195 key)
196 if not self.old_style:
197 hm.add_int(self.min_bits)
198 hm.add_int(self.preferred_bits)
199 if not self.old_style:
200 hm.add_int(self.max_bits)
201 hm.add_mpint(self.p)
202 hm.add_mpint(self.g)
203 hm.add_mpint(self.e)
204 hm.add_mpint(self.f)
205 hm.add_mpint(K)
206 H = SHA.new(str(hm)).digest()
207 self.transport._set_K_H(K, H)
208 # sign it
209 sig = self.transport.get_server_key().sign_ssh_data(self.transport.rng, H)
210 # send reply
211 m = Message()
212 m.add_byte(chr(_MSG_KEXDH_GEX_REPLY))
213 m.add_string(key)
214 m.add_mpint(self.f)
215 m.add_string(str(sig))
216 self.transport._send_message(m)
217 self.transport._activate_outbound()
218
220 host_key = m.get_string()
221 self.f = m.get_mpint()
222 sig = m.get_string()
223 if (self.f < 1) or (self.f > self.p - 1):
224 raise SSHException('Server kex "f" is out of range')
225 K = pow(self.f, self.x, self.p)
226 # 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)
227 hm = Message()
228 hm.add(self.transport.local_version, self.transport.remote_version,
229 self.transport.local_kex_init, self.transport.remote_kex_init,
230 host_key)
231 if not self.old_style:
232 hm.add_int(self.min_bits)
233 hm.add_int(self.preferred_bits)
234 if not self.old_style:
235 hm.add_int(self.max_bits)
236 hm.add_mpint(self.p)
237 hm.add_mpint(self.g)
238 hm.add_mpint(self.e)
239 hm.add_mpint(self.f)
240 hm.add_mpint(K)
241 self.transport._set_K_H(K, SHA.new(str(hm)).digest())
242 self.transport._verify_key(host_key, sig)
243 self.transport._activate_outbound()
244
| Home | Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Mon May 23 13:50:04 2011 | http://epydoc.sourceforge.net |