|
13 | 13 | # under the License. |
14 | 14 |
|
15 | 15 | import base64 |
16 | | -import ctypes |
17 | | -import ctypes.util |
18 | | -import struct |
19 | | -import sys |
20 | 16 |
|
21 | | -clib_path = ctypes.util.find_library("c") |
22 | | - |
23 | | -if sys.platform == "win32": |
24 | | - if clib_path: |
25 | | - clib = ctypes.CDLL(clib_path) |
26 | | - else: |
27 | | - clib = ctypes.cdll.ucrtbase |
28 | | - |
29 | | - # for backwards compatibility, try the older names |
30 | | - # libcrypto-1_1 comes bundled with PY 3.7 to 3.12 |
31 | | - ssl_lib_names = [ |
32 | | - "libcrypto-1_1", |
33 | | - "libcrypto", |
34 | | - "libeay32" |
35 | | - ] |
36 | | - |
37 | | - for ssl_lib_name in ssl_lib_names: |
38 | | - try: |
39 | | - openssl = ctypes.CDLL(ssl_lib_name) |
40 | | - break |
41 | | - except Exception: |
42 | | - pass |
43 | | -else: |
44 | | - clib = ctypes.CDLL(clib_path) |
45 | | - openssl_lib_path = ctypes.util.find_library("ssl") |
46 | | - openssl = ctypes.CDLL(openssl_lib_path) |
47 | | - |
48 | | - |
49 | | -class RSA(ctypes.Structure): |
50 | | - _fields_ = [ |
51 | | - ("pad", ctypes.c_int), |
52 | | - ("version", ctypes.c_long), |
53 | | - ("meth", ctypes.c_void_p), |
54 | | - ("engine", ctypes.c_void_p), |
55 | | - ("n", ctypes.c_void_p), |
56 | | - ("e", ctypes.c_void_p), |
57 | | - ("d", ctypes.c_void_p), |
58 | | - ("p", ctypes.c_void_p), |
59 | | - ("q", ctypes.c_void_p), |
60 | | - ("dmp1", ctypes.c_void_p), |
61 | | - ("dmq1", ctypes.c_void_p), |
62 | | - ("iqmp", ctypes.c_void_p), |
63 | | - ("sk", ctypes.c_void_p), |
64 | | - ("dummy", ctypes.c_int), |
65 | | - ("references", ctypes.c_int), |
66 | | - ("flags", ctypes.c_int), |
67 | | - ("_method_mod_n", ctypes.c_void_p), |
68 | | - ("_method_mod_p", ctypes.c_void_p), |
69 | | - ("_method_mod_q", ctypes.c_void_p), |
70 | | - ("bignum_data", ctypes.c_char_p), |
71 | | - ("blinding", ctypes.c_void_p), |
72 | | - ("mt_blinding", ctypes.c_void_p) |
73 | | - ] |
74 | | - |
75 | | - |
76 | | -openssl.RSA_PKCS1_PADDING = 1 |
77 | | - |
78 | | -openssl.RSA_new.restype = ctypes.POINTER(RSA) |
79 | | - |
80 | | -openssl.BN_bin2bn.restype = ctypes.c_void_p |
81 | | -openssl.BN_bin2bn.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p] |
82 | | - |
83 | | -openssl.BN_new.restype = ctypes.c_void_p |
84 | | - |
85 | | -openssl.RSA_size.restype = ctypes.c_int |
86 | | -openssl.RSA_size.argtypes = [ctypes.POINTER(RSA)] |
87 | | - |
88 | | -openssl.RSA_public_encrypt.argtypes = [ctypes.c_int, |
89 | | - ctypes.c_char_p, |
90 | | - ctypes.c_char_p, |
91 | | - ctypes.POINTER(RSA), |
92 | | - ctypes.c_int] |
93 | | -openssl.RSA_public_encrypt.restype = ctypes.c_int |
94 | | - |
95 | | -openssl.RSA_free.argtypes = [ctypes.POINTER(RSA)] |
96 | | - |
97 | | -openssl.PEM_write_RSAPublicKey.restype = ctypes.c_int |
98 | | -openssl.PEM_write_RSAPublicKey.argtypes = [ctypes.c_void_p, |
99 | | - ctypes.POINTER(RSA)] |
100 | | - |
101 | | -openssl.ERR_get_error.restype = ctypes.c_long |
102 | | -openssl.ERR_get_error.argtypes = [] |
103 | | - |
104 | | -openssl.ERR_error_string_n.restype = ctypes.c_void_p |
105 | | -openssl.ERR_error_string_n.argtypes = [ctypes.c_long, |
106 | | - ctypes.c_char_p, |
107 | | - ctypes.c_int] |
108 | | - |
109 | | -try: |
110 | | - openssl.ERR_load_crypto_strings.restype = ctypes.c_int |
111 | | - openssl.ERR_load_crypto_strings.argtypes = [] |
112 | | -except AttributeError: |
113 | | - # NOTE(avladu): This function is deprecated and no longer needed |
114 | | - # since OpenSSL 1.1 |
115 | | - pass |
116 | | - |
117 | | -clib.fopen.restype = ctypes.c_void_p |
118 | | -clib.fopen.argtypes = [ctypes.c_char_p, ctypes.c_char_p] |
119 | | - |
120 | | -clib.fclose.restype = ctypes.c_int |
121 | | -clib.fclose.argtypes = [ctypes.c_void_p] |
| 17 | +from cryptography.hazmat import backends |
| 18 | +from cryptography.hazmat.primitives.asymmetric import padding |
| 19 | +from cryptography.hazmat.primitives import serialization |
122 | 20 |
|
123 | 21 |
|
124 | 22 | class CryptException(Exception): |
125 | 23 | pass |
126 | 24 |
|
127 | 25 |
|
128 | | -class OpenSSLException(CryptException): |
129 | | - |
130 | | - def __init__(self): |
131 | | - message = self._get_openssl_error_msg() |
132 | | - super(OpenSSLException, self).__init__(message) |
133 | | - |
134 | | - def _get_openssl_error_msg(self): |
135 | | - try: |
136 | | - openssl.ERR_load_crypto_strings() |
137 | | - except AttributeError: |
138 | | - pass |
139 | | - |
140 | | - errno = openssl.ERR_get_error() |
141 | | - errbuf = ctypes.create_string_buffer(1024) |
142 | | - openssl.ERR_error_string_n(errno, errbuf, 1024) |
143 | | - return errbuf.value.decode("ascii") |
144 | | - |
145 | | - |
146 | | -class RSAWrapper(object): |
147 | | - |
148 | | - def __init__(self, rsa_p): |
149 | | - self._rsa_p = rsa_p |
150 | | - |
151 | | - def __enter__(self): |
152 | | - return self |
153 | | - |
154 | | - def __exit__(self, tp, value, tb): |
155 | | - self.free() |
156 | | - |
157 | | - def free(self): |
158 | | - openssl.RSA_free(self._rsa_p) |
159 | | - |
160 | | - def public_encrypt(self, clear_text): |
161 | | - flen = len(clear_text) |
162 | | - rsa_size = openssl.RSA_size(self._rsa_p) |
163 | | - enc_text = ctypes.create_string_buffer(rsa_size) |
164 | | - |
165 | | - enc_text_len = openssl.RSA_public_encrypt(flen, |
166 | | - clear_text, |
167 | | - enc_text, |
168 | | - self._rsa_p, |
169 | | - openssl.RSA_PKCS1_PADDING) |
170 | | - if enc_text_len == -1: |
171 | | - raise OpenSSLException() |
172 | | - |
173 | | - return enc_text[:enc_text_len] |
174 | | - |
175 | | - |
176 | 26 | class CryptManager(object): |
177 | 27 |
|
178 | | - def load_ssh_rsa_public_key(self, ssh_pub_key): |
| 28 | + def public_encrypt(self, ssh_pub_key, password): |
179 | 29 | ssh_rsa_prefix = "ssh-rsa " |
180 | 30 |
|
181 | 31 | if not ssh_pub_key.startswith(ssh_rsa_prefix): |
182 | 32 | raise CryptException('Invalid SSH key') |
183 | 33 |
|
184 | | - s = ssh_pub_key[len(ssh_rsa_prefix):] |
185 | | - idx = s.find(' ') |
186 | | - if idx >= 0: |
187 | | - b64_pub_key = s[:idx] |
188 | | - else: |
189 | | - b64_pub_key = s |
190 | | - |
191 | | - pub_key = base64.b64decode(b64_pub_key) |
192 | | - |
193 | | - offset = 0 |
194 | | - |
195 | | - key_type_len = struct.unpack('>I', pub_key[offset:offset + 4])[0] |
196 | | - offset += 4 |
197 | | - |
198 | | - key_type = pub_key[offset:offset + key_type_len].decode('utf-8') |
199 | | - offset += key_type_len |
200 | | - |
201 | | - if key_type not in ['ssh-rsa', 'rsa', 'rsa1']: |
202 | | - raise CryptException('Unsupported SSH key type "%s". ' |
203 | | - 'Only RSA keys are currently supported' |
204 | | - % key_type) |
205 | | - |
206 | | - rsa_p = openssl.RSA_new() |
207 | | - try: |
208 | | - rsa_p.contents.e = openssl.BN_new() |
209 | | - rsa_p.contents.n = openssl.BN_new() |
210 | | - |
211 | | - e_len = struct.unpack('>I', pub_key[offset:offset + 4])[0] |
212 | | - offset += 4 |
213 | | - |
214 | | - e_key_bin = pub_key[offset:offset + e_len] |
215 | | - offset += e_len |
216 | | - |
217 | | - if not openssl.BN_bin2bn(e_key_bin, e_len, rsa_p.contents.e): |
218 | | - raise OpenSSLException() |
219 | | - |
220 | | - n_len = struct.unpack('>I', pub_key[offset:offset + 4])[0] |
221 | | - offset += 4 |
222 | | - |
223 | | - n_key_bin = pub_key[offset:offset + n_len] |
224 | | - offset += n_len |
225 | | - |
226 | | - if offset != len(pub_key): |
227 | | - raise CryptException('Invalid SSH key') |
228 | | - |
229 | | - if not openssl.BN_bin2bn(n_key_bin, n_len, rsa_p.contents.n): |
230 | | - raise OpenSSLException() |
231 | | - |
232 | | - return RSAWrapper(rsa_p) |
233 | | - except Exception: |
234 | | - openssl.RSA_free(rsa_p) |
235 | | - raise |
| 34 | + rsa_public_key = serialization.load_ssh_public_key( |
| 35 | + ssh_pub_key.encode(), backends.default_backend()) |
| 36 | + enc_password = rsa_public_key.encrypt( |
| 37 | + password.encode(), |
| 38 | + padding.PKCS1v15() |
| 39 | + ) |
| 40 | + return base64.b64encode(enc_password) |
0 commit comments