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
« 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.
19"""
20RSA keys.
21"""
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
28from paramiko.message import Message
29from paramiko.pkey import PKey
30from paramiko.ssh_exception import SSHException
33class RSAKey(PKey):
34 """
35 Representation of an RSA key which can be used to sign and verify SSH2
36 data.
37 """
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 }
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())
81 @property
82 def size(self):
83 return self.key.key_size
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()
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()
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")
104 @property
105 def _fields(self):
106 return (self.get_name(), self.public_numbers.e, self.public_numbers.n)
108 def get_name(self):
109 return "ssh-rsa"
111 def get_bits(self):
112 return self.size
114 def can_sign(self):
115 return isinstance(self.key, rsa.RSAPrivateKey)
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
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()
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
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
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 )
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 )
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.
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)
183 # ...internals...
185 def _from_private_key_file(self, filename, password):
186 data = self._read_private_key_file("RSA", filename, password)
187 self._decode_key(data)
189 def _from_private_key(self, file_obj, password):
190 data = self._read_private_key("RSA", file_obj, password)
191 self._decode_key(data)
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