35
35
hash = hashlib.sha512
39
"""Convert a dictionary of string key/values into a string."""
42
for k, v in d.iteritems():
43
assert isinstance(k, str), k
45
parts.append(chr(l>>8))
46
parts.append(chr(l&255))
49
assert isinstance(v, str), v
51
parts.append(chr(l>>8))
52
parts.append(chr(l&255))
58
blob_size.append(chr(l>>24))
59
blob_size.append(chr(l>>16&255))
60
blob_size.append(chr(l>>8&255))
61
blob_size.append(chr(l&255))
63
return "CMbydi0" + "".join(blob_size) + blob
67
"""Convert a string from C{dict_to_bytes} back into a dictionary."""
68
if b[:7] != "CMbydi0":
69
raise ValueError("magic bytes missing. Invalid string. %r", b[:10])
74
blob_size = (blob_size << 8) + ord(c)
77
if blob_size != len(blob):
78
raise ValueError("bytes are corrupt; expected %d, got %d" % (blob_size,
84
while blob_cursor < blob_size:
85
k_len = (ord(blob[blob_cursor+0])<<8) + ord(blob[blob_cursor+1])
86
k = blob[blob_cursor+2:blob_cursor+2+k_len]
87
blob_cursor += k_len + 2
88
v_len = (ord(blob[blob_cursor+0])<<8) + ord(blob[blob_cursor+1])
89
v = blob[blob_cursor+2:blob_cursor+2+v_len]
90
blob_cursor += v_len + 2
38
95
class ListenForInvitations():
39
96
"""Narrative "Alice".
41
98
This is the first half of a TCP listening socket. We spawn off
42
99
processors when we accept invitation-connections."""
44
def __init__(self, get_secret_from_user, on_close):
101
def __init__(self, get_secret_from_user, on_close, hostid, oauth_data):
46
103
self.logging = logging.getLogger(self.__class__.__name__)
48
105
self.factory = ProcessAnInvitationFactory(get_secret_from_user,
106
on_close, hostid, oauth_data)
50
107
self.listening_port = reactor.listenTCP(0, self.factory)
52
109
def get_local_port(self):
84
141
self.logging.debug("connection lost")
85
142
basic.LineReceiver.connectionLost(self, reason)
87
def lineReceived(self, message):
144
def lineReceived(self, rich_message):
88
145
"""Handler for receipt of a message from the Bob end."""
90
digest_nybble_count = h.digest_size * 2
91
self.expected_hash = message[0:digest_nybble_count]
92
self.public_seed = message[digest_nybble_count:]
146
d = bytes_to_dict(rich_message)
148
self.expected_hash = d.pop("secret_message")
149
self.public_seed = d.pop("public_seed")
150
remote_hostid = d.pop("hostid")
93
153
self.factory.get_secret_from_user(self.transport.getPeer().host,
94
154
self.check_secret_from_user,
95
self.send_secret_to_remote)
155
self.send_secret_to_remote,
156
remote_hostid, remote_oauth)
97
158
def send_secret_to_remote(self, secret_message):
98
159
"""A callback for the invitation protocol to start a new phase
102
163
h.update(self.public_seed)
103
164
h.update(secret_message)
104
self.sendLine(h.hexdigest())
166
all_dict.update(self.factory.oauth_info)
167
all_dict["hostid"] = self.factory.hostid
168
all_dict["secret_message"] = h.hexdigest()
169
self.sendLine(dict_to_bytes(all_dict))
106
171
def check_secret_from_user(self, secret_message):
107
172
"""A callback for the invitation protocol to verify the secret
108
173
that the user gives, against the hash we received over the
111
177
h.update(secret_message)
112
178
digest = h.hexdigest()
116
182
h.update(self.public_seed)
117
183
h.update(secret_message)
118
self.sendLine(h.hexdigest())
185
all_dict.update(self.factory.oauth_info)
186
all_dict["hostid"] = self.factory.hostid
187
all_dict["secret_message"] = h.hexdigest()
188
self.sendLine(dict_to_bytes(all_dict))
120
190
self.logging.debug("User knew secret!")
133
203
protocol = ProcessAnInvitationProtocol
135
def __init__(self, get_secret_from_user, on_close):
205
def __init__(self, get_secret_from_user, on_close, hostid, oauth_info):
136
206
self.logging = logging.getLogger(self.__class__.__name__)
137
207
self.get_secret_from_user = get_secret_from_user
138
208
self.on_close = on_close
210
self.oauth_info = oauth_info
141
213
class SendInvitationProtocol(basic.LineReceiver):
155
227
h.update(self.factory.secret_message)
156
self.sendLine(h.hexdigest() + self.factory.public_seed)
228
d = dict(secret_message=h.hexdigest(),
229
public_seed=self.factory.public_seed,
230
hostid=self.factory.local_hostid)
231
d.update(self.factory.local_oauth_info)
232
self.sendLine(dict_to_bytes(d))
159
235
h.update(self.factory.public_seed)
161
237
self.expected_hash_of_secret = h.hexdigest()
164
def lineReceived(self, message):
240
def lineReceived(self, rich_message):
165
241
"""Handler for receipt of a message from the Alice end."""
242
d = bytes_to_dict(rich_message)
243
message = d.pop("secret_message")
166
245
if message == self.expected_hash_of_secret:
167
246
remote_host = self.transport.getPeer().host
169
248
remote_hostname = get_remote_hostname(remote_host)
170
249
except dbus.exceptions.DBusException:
171
250
remote_hostname = None
172
self.factory.auth_complete_cb(remote_hostname, "(port)", "(info)")
251
remote_hostid = d.pop("hostid")
252
self.factory.auth_complete_cb(remote_hostname, remote_hostid, d)
173
253
self.transport.loseConnection()
175
255
self.logging.warn("Expected %r from invitation.",
188
268
protocol = SendInvitationProtocol
190
270
def __init__(self, auth_complete_cb, secret_message, public_seed,
271
on_close, local_hostid, local_oauth_info):
192
272
self.logging = logging.getLogger(self.__class__.__name__)
193
273
self.auth_complete_cb = auth_complete_cb
194
274
self.secret_message = secret_message
195
275
self.public_seed = public_seed
196
276
self.on_close = on_close
277
self.local_hostid = local_hostid
278
self.local_oauth_info = local_oauth_info
197
279
self.logging.debug("initialized")
216
298
def start_send_invitation(host, port, auth_complete_cb, secret_message,
217
public_seed, on_close):
299
public_seed, on_close, local_hostid, local_oauth):
218
300
"""Instantiate the factory to hold configuration data about sending an
219
301
invitation and let the reactor add it to its event-handling loop by way of
220
302
starting a TCP connection."""
221
303
factory = SendInvitationFactory(auth_complete_cb, secret_message,
222
public_seed, on_close)
304
public_seed, on_close, local_hostid, local_oauth)
223
305
reactor.connectTCP(host, port, factory)