| 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
40
42
43 name = 'diffie-hellman-group-exchange-sha1'
44 min_bits = 1024
45 max_bits = 8192
46 preferred_bits = 2048
47
49 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 = False
57
59 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)
78
80 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...
93
95 # 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 = 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(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)
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(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 = 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(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)
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 = 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()
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, sha1(hm.asbytes()).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 Thu May 7 11:49:48 2015 | http://epydoc.sourceforge.net |