|
1 | 1 | from __future__ import absolute_import, division
|
2 | 2 |
|
3 |
| -import base64 |
4 | 3 | import copy
|
5 | 4 | import errno
|
6 |
| -import hashlib |
7 |
| -import hmac |
8 | 5 | import io
|
9 | 6 | import logging
|
10 | 7 | from random import shuffle, uniform
|
11 | 8 |
|
12 |
| -from uuid import uuid4 |
13 |
| - |
14 | 9 | # selectors in stdlib as of py3.4
|
15 | 10 | try:
|
16 | 11 | import selectors # pylint: disable=import-error
|
|
34 | 29 | from kafka.protocol.metadata import MetadataRequest
|
35 | 30 | from kafka.protocol.parser import KafkaProtocol
|
36 | 31 | from kafka.protocol.types import Int32, Int8
|
| 32 | +from kafka.scram import ScramClient |
37 | 33 | from kafka.version import __version__
|
38 | 34 |
|
39 | 35 |
|
|
42 | 38 | TimeoutError = socket.error
|
43 | 39 | BlockingIOError = Exception
|
44 | 40 |
|
45 |
| - def xor_bytes(left, right): |
46 |
| - return bytearray(ord(lb) ^ ord(rb) for lb, rb in zip(left, right)) |
47 |
| -else: |
48 |
| - def xor_bytes(left, right): |
49 |
| - return bytes(lb ^ rb for lb, rb in zip(left, right)) |
50 |
| - |
51 | 41 | log = logging.getLogger(__name__)
|
52 | 42 |
|
53 | 43 | DEFAULT_KAFKA_PORT = 9092
|
@@ -107,69 +97,6 @@ class ConnectionStates(object):
|
107 | 97 | AUTHENTICATING = '<authenticating>'
|
108 | 98 |
|
109 | 99 |
|
110 |
| -class ScramClient: |
111 |
| - MECHANISMS = { |
112 |
| - 'SCRAM-SHA-256': hashlib.sha256, |
113 |
| - 'SCRAM-SHA-512': hashlib.sha512 |
114 |
| - } |
115 |
| - |
116 |
| - def __init__(self, user, password, mechanism): |
117 |
| - self.nonce = str(uuid4()).replace('-', '') |
118 |
| - self.auth_message = '' |
119 |
| - self.salted_password = None |
120 |
| - self.user = user |
121 |
| - self.password = password.encode() |
122 |
| - self.hashfunc = self.MECHANISMS[mechanism] |
123 |
| - self.hashname = ''.join(mechanism.lower().split('-')[1:3]) |
124 |
| - self.stored_key = None |
125 |
| - self.client_key = None |
126 |
| - self.client_signature = None |
127 |
| - self.client_proof = None |
128 |
| - self.server_key = None |
129 |
| - self.server_signature = None |
130 |
| - |
131 |
| - def first_message(self): |
132 |
| - client_first_bare = 'n={},r={}'.format(self.user, self.nonce) |
133 |
| - self.auth_message += client_first_bare |
134 |
| - return 'n,,' + client_first_bare |
135 |
| - |
136 |
| - def process_server_first_message(self, server_first_message): |
137 |
| - self.auth_message += ',' + server_first_message |
138 |
| - params = dict(pair.split('=', 1) for pair in server_first_message.split(',')) |
139 |
| - server_nonce = params['r'] |
140 |
| - if not server_nonce.startswith(self.nonce): |
141 |
| - raise ValueError("Server nonce, did not start with client nonce!") |
142 |
| - self.nonce = server_nonce |
143 |
| - self.auth_message += ',c=biws,r=' + self.nonce |
144 |
| - |
145 |
| - salt = base64.b64decode(params['s'].encode()) |
146 |
| - iterations = int(params['i']) |
147 |
| - self.create_salted_password(salt, iterations) |
148 |
| - |
149 |
| - self.client_key = self.hmac(self.salted_password, b'Client Key') |
150 |
| - self.stored_key = self.hashfunc(self.client_key).digest() |
151 |
| - self.client_signature = self.hmac(self.stored_key, self.auth_message.encode()) |
152 |
| - self.client_proof = xor_bytes(self.client_key, self.client_signature) |
153 |
| - self.server_key = self.hmac(self.salted_password, b'Server Key') |
154 |
| - self.server_signature = self.hmac(self.server_key, self.auth_message.encode()) |
155 |
| - |
156 |
| - def hmac(self, key, msg): |
157 |
| - return hmac.new(key, msg, digestmod=self.hashfunc).digest() |
158 |
| - |
159 |
| - def create_salted_password(self, salt, iterations): |
160 |
| - self.salted_password = hashlib.pbkdf2_hmac( |
161 |
| - self.hashname, self.password, salt, iterations |
162 |
| - ) |
163 |
| - |
164 |
| - def final_message(self): |
165 |
| - client_final_no_proof = 'c=biws,r=' + self.nonce |
166 |
| - return 'c=biws,r={},p={}'.format(self.nonce, base64.b64encode(self.client_proof).decode()) |
167 |
| - |
168 |
| - def process_server_final_message(self, server_final_message): |
169 |
| - params = dict(pair.split('=', 1) for pair in server_final_message.split(',')) |
170 |
| - if self.server_signature != base64.b64decode(params['v'].encode()): |
171 |
| - raise ValueError("Server sent wrong signature!") |
172 |
| - |
173 | 100 | class BrokerConnection(object):
|
174 | 101 | """Initialize a Kafka broker connection
|
175 | 102 |
|
@@ -747,20 +674,20 @@ def _try_authenticate_scram(self, future):
|
747 | 674 | close = False
|
748 | 675 | else:
|
749 | 676 | try:
|
750 |
| - client_first = scram_client.first_message().encode() |
| 677 | + client_first = scram_client.first_message().encode('utf-8') |
751 | 678 | size = Int32.encode(len(client_first))
|
752 | 679 | self._send_bytes_blocking(size + client_first)
|
753 | 680 |
|
754 | 681 | (data_len,) = struct.unpack('>i', self._recv_bytes_blocking(4))
|
755 |
| - server_first = self._recv_bytes_blocking(data_len).decode() |
| 682 | + server_first = self._recv_bytes_blocking(data_len).decode('utf-8') |
756 | 683 | scram_client.process_server_first_message(server_first)
|
757 | 684 |
|
758 |
| - client_final = scram_client.final_message().encode() |
| 685 | + client_final = scram_client.final_message().encode('utf-8') |
759 | 686 | size = Int32.encode(len(client_final))
|
760 | 687 | self._send_bytes_blocking(size + client_final)
|
761 | 688 |
|
762 | 689 | (data_len,) = struct.unpack('>i', self._recv_bytes_blocking(4))
|
763 |
| - server_final = self._recv_bytes_blocking(data_len).decode() |
| 690 | + server_final = self._recv_bytes_blocking(data_len).decode('utf-8') |
764 | 691 | scram_client.process_server_final_message(server_final)
|
765 | 692 |
|
766 | 693 | except (ConnectionError, TimeoutError) as e:
|
|
0 commit comments