Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/kex_group1.py: 26%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

86 statements  

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# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 

18 

19""" 

20Standard SSH key exchange ("kex" if you wanna sound cool). Diffie-Hellman of 

211024 bit key halves, using a known "p" prime and "g" generator. 

22""" 

23 

24import os 

25from hashlib import sha1 

26 

27from paramiko import util 

28from paramiko.common import max_byte, zero_byte, byte_chr, byte_mask 

29from paramiko.message import Message 

30from paramiko.ssh_exception import SSHException 

31 

32 

33_MSG_KEXDH_INIT, _MSG_KEXDH_REPLY = range(30, 32) 

34c_MSG_KEXDH_INIT, c_MSG_KEXDH_REPLY = [byte_chr(c) for c in range(30, 32)] 

35 

36b7fffffffffffffff = byte_chr(0x7F) + max_byte * 7 

37b0000000000000000 = zero_byte * 8 

38 

39 

40class KexGroup1: 

41 

42 # draft-ietf-secsh-transport-09.txt, page 17 

43 P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa 

44 G = 2 

45 

46 name = "diffie-hellman-group1-sha1" 

47 hash_algo = sha1 

48 

49 def __init__(self, transport): 

50 self.transport = transport 

51 self.x = 0 

52 self.e = 0 

53 self.f = 0 

54 

55 def start_kex(self): 

56 self._generate_x() 

57 if self.transport.server_mode: 

58 # compute f = g^x mod p, but don't send it yet 

59 self.f = pow(self.G, self.x, self.P) 

60 self.transport._expect_packet(_MSG_KEXDH_INIT) 

61 return 

62 # compute e = g^x mod p (where g=2), and send it 

63 self.e = pow(self.G, self.x, self.P) 

64 m = Message() 

65 m.add_byte(c_MSG_KEXDH_INIT) 

66 m.add_mpint(self.e) 

67 self.transport._send_message(m) 

68 self.transport._expect_packet(_MSG_KEXDH_REPLY) 

69 

70 def parse_next(self, ptype, m): 

71 if self.transport.server_mode and (ptype == _MSG_KEXDH_INIT): 

72 return self._parse_kexdh_init(m) 

73 elif not self.transport.server_mode and (ptype == _MSG_KEXDH_REPLY): 

74 return self._parse_kexdh_reply(m) 

75 msg = "KexGroup1 asked to handle packet type {:d}" 

76 raise SSHException(msg.format(ptype)) 

77 

78 # ...internals... 

79 

80 def _generate_x(self): 

81 # generate an "x" (1 < x < q), where q is (p-1)/2. 

82 # p is a 128-byte (1024-bit) number, where the first 64 bits are 1. 

83 # therefore q can be approximated as a 2^1023. we drop the subset of 

84 # potential x where the first 63 bits are 1, because some of those 

85 # will be larger than q (but this is a tiny tiny subset of 

86 # potential x). 

87 while 1: 

88 x_bytes = os.urandom(128) 

89 x_bytes = byte_mask(x_bytes[0], 0x7F) + x_bytes[1:] 

90 if ( 

91 x_bytes[:8] != b7fffffffffffffff 

92 and x_bytes[:8] != b0000000000000000 

93 ): 

94 break 

95 self.x = util.inflate_long(x_bytes) 

96 

97 def _parse_kexdh_reply(self, m): 

98 # client mode 

99 host_key = m.get_string() 

100 self.f = m.get_mpint() 

101 if (self.f < 1) or (self.f > self.P - 1): 

102 raise SSHException('Server kex "f" is out of range') 

103 sig = m.get_binary() 

104 K = pow(self.f, self.x, self.P) 

105 # okay, build up the hash H of 

106 # (V_C || V_S || I_C || I_S || K_S || e || f || K) 

107 hm = Message() 

108 hm.add( 

109 self.transport.local_version, 

110 self.transport.remote_version, 

111 self.transport.local_kex_init, 

112 self.transport.remote_kex_init, 

113 ) 

114 hm.add_string(host_key) 

115 hm.add_mpint(self.e) 

116 hm.add_mpint(self.f) 

117 hm.add_mpint(K) 

118 self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest()) 

119 self.transport._verify_key(host_key, sig) 

120 self.transport._activate_outbound() 

121 

122 def _parse_kexdh_init(self, m): 

123 # server mode 

124 self.e = m.get_mpint() 

125 if (self.e < 1) or (self.e > self.P - 1): 

126 raise SSHException('Client kex "e" is out of range') 

127 K = pow(self.e, self.x, self.P) 

128 key = self.transport.get_server_key().asbytes() 

129 # okay, build up the hash H of 

130 # (V_C || V_S || I_C || I_S || K_S || e || f || K) 

131 hm = Message() 

132 hm.add( 

133 self.transport.remote_version, 

134 self.transport.local_version, 

135 self.transport.remote_kex_init, 

136 self.transport.local_kex_init, 

137 ) 

138 hm.add_string(key) 

139 hm.add_mpint(self.e) 

140 hm.add_mpint(self.f) 

141 hm.add_mpint(K) 

142 H = self.hash_algo(hm.asbytes()).digest() 

143 self.transport._set_K_H(K, H) 

144 # sign it 

145 sig = self.transport.get_server_key().sign_ssh_data( 

146 H, self.transport.host_key_type 

147 ) 

148 # send reply 

149 m = Message() 

150 m.add_byte(c_MSG_KEXDH_REPLY) 

151 m.add_string(key) 

152 m.add_mpint(self.f) 

153 m.add_string(sig) 

154 self.transport._send_message(m) 

155 self.transport._activate_outbound()