Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/rsakey.py: 31%

100 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:36 +0000

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""" 

20RSA keys. 

21""" 

22 

23from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm 

24from cryptography.hazmat.backends import default_backend 

25from cryptography.hazmat.primitives import hashes, serialization 

26from cryptography.hazmat.primitives.asymmetric import rsa, padding 

27 

28from paramiko.message import Message 

29from paramiko.pkey import PKey 

30from paramiko.ssh_exception import SSHException 

31 

32 

33class RSAKey(PKey): 

34 """ 

35 Representation of an RSA key which can be used to sign and verify SSH2 

36 data. 

37 """ 

38 

39 HASHES = { 

40 "ssh-rsa": hashes.SHA1, 

41 "ssh-rsa-cert-v01@openssh.com": hashes.SHA1, 

42 "rsa-sha2-256": hashes.SHA256, 

43 "rsa-sha2-256-cert-v01@openssh.com": hashes.SHA256, 

44 "rsa-sha2-512": hashes.SHA512, 

45 "rsa-sha2-512-cert-v01@openssh.com": hashes.SHA512, 

46 } 

47 

48 def __init__( 

49 self, 

50 msg=None, 

51 data=None, 

52 filename=None, 

53 password=None, 

54 key=None, 

55 file_obj=None, 

56 ): 

57 self.key = None 

58 self.public_blob = None 

59 if file_obj is not None: 

60 self._from_private_key(file_obj, password) 

61 return 

62 if filename is not None: 

63 self._from_private_key_file(filename, password) 

64 return 

65 if (msg is None) and (data is not None): 

66 msg = Message(data) 

67 if key is not None: 

68 self.key = key 

69 else: 

70 self._check_type_and_load_cert( 

71 msg=msg, 

72 # NOTE: this does NOT change when using rsa2 signatures; it's 

73 # purely about key loading, not exchange or verification 

74 key_type="ssh-rsa", 

75 cert_type="ssh-rsa-cert-v01@openssh.com", 

76 ) 

77 self.key = rsa.RSAPublicNumbers( 

78 e=msg.get_mpint(), n=msg.get_mpint() 

79 ).public_key(default_backend()) 

80 

81 @property 

82 def size(self): 

83 return self.key.key_size 

84 

85 @property 

86 def public_numbers(self): 

87 if isinstance(self.key, rsa.RSAPrivateKey): 

88 return self.key.private_numbers().public_numbers 

89 else: 

90 return self.key.public_numbers() 

91 

92 def asbytes(self): 

93 m = Message() 

94 m.add_string("ssh-rsa") 

95 m.add_mpint(self.public_numbers.e) 

96 m.add_mpint(self.public_numbers.n) 

97 return m.asbytes() 

98 

99 def __str__(self): 

100 # NOTE: see #853 to explain some legacy behavior. 

101 # TODO 4.0: replace with a nice clean fingerprint display or something 

102 return self.asbytes().decode("utf8", errors="ignore") 

103 

104 @property 

105 def _fields(self): 

106 return (self.get_name(), self.public_numbers.e, self.public_numbers.n) 

107 

108 def get_name(self): 

109 return "ssh-rsa" 

110 

111 def get_bits(self): 

112 return self.size 

113 

114 def can_sign(self): 

115 return isinstance(self.key, rsa.RSAPrivateKey) 

116 

117 def sign_ssh_data(self, data, algorithm="ssh-rsa"): 

118 sig = self.key.sign( 

119 data, 

120 padding=padding.PKCS1v15(), 

121 algorithm=self.HASHES[algorithm](), 

122 ) 

123 m = Message() 

124 m.add_string(algorithm.replace("-cert-v01@openssh.com", "")) 

125 m.add_string(sig) 

126 return m 

127 

128 def verify_ssh_sig(self, data, msg): 

129 sig_algorithm = msg.get_text() 

130 if sig_algorithm not in self.HASHES: 

131 return False 

132 key = self.key 

133 if isinstance(key, rsa.RSAPrivateKey): 

134 key = key.public_key() 

135 

136 # NOTE: pad received signature with leading zeros, key.verify() 

137 # expects a signature of key size (e.g. PuTTY doesn't pad) 

138 sign = msg.get_binary() 

139 diff = key.key_size - len(sign) * 8 

140 if diff > 0: 

141 sign = b"\x00" * ((diff + 7) // 8) + sign 

142 

143 try: 

144 key.verify( 

145 sign, data, padding.PKCS1v15(), self.HASHES[sig_algorithm]() 

146 ) 

147 except InvalidSignature: 

148 return False 

149 else: 

150 return True 

151 

152 def write_private_key_file(self, filename, password=None): 

153 self._write_private_key_file( 

154 filename, 

155 self.key, 

156 serialization.PrivateFormat.TraditionalOpenSSL, 

157 password=password, 

158 ) 

159 

160 def write_private_key(self, file_obj, password=None): 

161 self._write_private_key( 

162 file_obj, 

163 self.key, 

164 serialization.PrivateFormat.TraditionalOpenSSL, 

165 password=password, 

166 ) 

167 

168 @staticmethod 

169 def generate(bits, progress_func=None): 

170 """ 

171 Generate a new private RSA key. This factory function can be used to 

172 generate a new host key or authentication key. 

173 

174 :param int bits: number of bits the generated key should be. 

175 :param progress_func: Unused 

176 :return: new `.RSAKey` private key 

177 """ 

178 key = rsa.generate_private_key( 

179 public_exponent=65537, key_size=bits, backend=default_backend() 

180 ) 

181 return RSAKey(key=key) 

182 

183 # ...internals... 

184 

185 def _from_private_key_file(self, filename, password): 

186 data = self._read_private_key_file("RSA", filename, password) 

187 self._decode_key(data) 

188 

189 def _from_private_key(self, file_obj, password): 

190 data = self._read_private_key("RSA", file_obj, password) 

191 self._decode_key(data) 

192 

193 def _decode_key(self, data): 

194 pkformat, data = data 

195 if pkformat == self._PRIVATE_KEY_FORMAT_ORIGINAL: 

196 try: 

197 key = serialization.load_der_private_key( 

198 data, password=None, backend=default_backend() 

199 ) 

200 except (ValueError, TypeError, UnsupportedAlgorithm) as e: 

201 raise SSHException(str(e)) 

202 elif pkformat == self._PRIVATE_KEY_FORMAT_OPENSSH: 

203 n, e, d, iqmp, p, q = self._uint32_cstruct_unpack(data, "iiiiii") 

204 public_numbers = rsa.RSAPublicNumbers(e=e, n=n) 

205 key = rsa.RSAPrivateNumbers( 

206 p=p, 

207 q=q, 

208 d=d, 

209 dmp1=d % (p - 1), 

210 dmq1=d % (q - 1), 

211 iqmp=iqmp, 

212 public_numbers=public_numbers, 

213 ).private_key(default_backend()) 

214 else: 

215 self._got_bad_key_format_id(pkformat) 

216 assert isinstance(key, rsa.RSAPrivateKey) 

217 self.key = key