rename channel-id to channelid. changes DB schema.

This commit is contained in:
Brian Warner 2015-10-06 16:16:41 -07:00
parent fc641622ba
commit fc30fa6cd4
5 changed files with 98 additions and 98 deletions

View File

@ -15,8 +15,8 @@ SECOND = 1
MINUTE = 60*SECOND MINUTE = 60*SECOND
# relay URLs are: # relay URLs are:
# GET /list -> {channel-ids: [INT..]} # GET /list -> {channelids: [INT..]}
# POST /allocate {side: SIDE} -> {channel-id: INT} # POST /allocate {side: SIDE} -> {channelid: INT}
# these return all messages (base64) for CID= : # these return all messages (base64) for CID= :
# POST /CID {side:, phase:, body:} -> {messages: [{phase:, body:}..]} # POST /CID {side:, phase:, body:} -> {messages: [{phase:, body:}..]}
# GET /CID (no-eventsource) -> {messages: [{phase:, body:}..]} # GET /CID (no-eventsource) -> {messages: [{phase:, body:}..]}
@ -25,8 +25,8 @@ MINUTE = 60*SECOND
# all JSON responses include a "welcome:{..}" key # all JSON responses include a "welcome:{..}" key
class Channel: class Channel:
def __init__(self, relay, channel_id, side, handle_welcome): def __init__(self, relay, channelid, side, handle_welcome):
self._channel_url = "%s%d" % (relay, channel_id) self._channel_url = "%s%d" % (relay, channelid)
self._side = side self._side = side
self._handle_welcome = handle_welcome self._handle_welcome = handle_welcome
self._messages = set() # (phase,body) , body is bytes self._messages = set() # (phase,body) , body is bytes
@ -107,8 +107,8 @@ class ChannelManager:
def list_channels(self): def list_channels(self):
r = requests.get(self._relay + "list") r = requests.get(self._relay + "list")
r.raise_for_status() r.raise_for_status()
channel_ids = r.json()["channel-ids"] channelids = r.json()["channelids"]
return channel_ids return channelids
def allocate(self): def allocate(self):
data = json.dumps({"side": self._side}).encode("utf-8") data = json.dumps({"side": self._side}).encode("utf-8")
@ -117,11 +117,11 @@ class ChannelManager:
data = r.json() data = r.json()
if "welcome" in data: if "welcome" in data:
self._handle_welcome(data["welcome"]) self._handle_welcome(data["welcome"])
channel_id = data["channel-id"] channelid = data["channelid"]
return channel_id return channelid
def connect(self, channel_id): def connect(self, channelid):
return Channel(self._relay, channel_id, self._side, return Channel(self._relay, channelid, self._side,
self._handle_welcome) self._handle_welcome)
class Wormhole: class Wormhole:
@ -167,10 +167,10 @@ class Wormhole:
def get_code(self, code_length=2): def get_code(self, code_length=2):
if self.code is not None: raise UsageError if self.code is not None: raise UsageError
channel_id = self._channel_manager.allocate() channelid = self._channel_manager.allocate()
code = codes.make_code(channel_id, code_length) code = codes.make_code(channelid, code_length)
assert isinstance(code, str), type(code) assert isinstance(code, str), type(code)
self._set_code_and_channel_id(code) self._set_code_and_channelid(code)
self._start() self._start()
return code return code
@ -183,17 +183,17 @@ class Wormhole:
def set_code(self, code): # used for human-made pre-generated codes def set_code(self, code): # used for human-made pre-generated codes
if not isinstance(code, str): raise UsageError if not isinstance(code, str): raise UsageError
if self.code is not None: raise UsageError if self.code is not None: raise UsageError
self._set_code_and_channel_id(code) self._set_code_and_channelid(code)
self._start() self._start()
def _set_code_and_channel_id(self, code): def _set_code_and_channelid(self, code):
if self.code is not None: raise UsageError if self.code is not None: raise UsageError
mo = re.search(r'^(\d+)-', code) mo = re.search(r'^(\d+)-', code)
if not mo: if not mo:
raise ValueError("code (%s) must start with NN-" % code) raise ValueError("code (%s) must start with NN-" % code)
self.code = code self.code = code
channel_id = int(mo.group(1)) channelid = int(mo.group(1))
self.channel = self._channel_manager.connect(channel_id) self.channel = self._channel_manager.connect(channelid)
def _start(self): def _start(self):
# allocate the rest now too, so it can be serialized # allocate the rest now too, so it can be serialized

View File

@ -9,17 +9,17 @@ CREATE TABLE `version`
CREATE TABLE `messages` CREATE TABLE `messages`
( (
`channel_id` INTEGER, `channelid` INTEGER,
`side` VARCHAR, `side` VARCHAR,
`phase` VARCHAR, -- not numeric, more of a PAKE-phase indicator string `phase` VARCHAR, -- not numeric, more of a PAKE-phase indicator string
`body` VARCHAR, `body` VARCHAR,
`when` INTEGER `when` INTEGER
); );
CREATE INDEX `messages_idx` ON `messages` (`channel_id`, `side`, `phase`); CREATE INDEX `messages_idx` ON `messages` (`channelid`, `side`, `phase`);
CREATE TABLE `allocations` CREATE TABLE `allocations`
( (
`channel_id` INTEGER, `channelid` INTEGER,
`side` VARCHAR `side` VARCHAR
); );
CREATE INDEX `allocations_idx` ON `allocations` (`channel_id`); CREATE INDEX `allocations_idx` ON `allocations` (`channelid`);

View File

@ -47,8 +47,8 @@ class EventsProtocol:
# note: no versions of IE (including the current IE11) support EventSource # note: no versions of IE (including the current IE11) support EventSource
# relay URLs are: # relay URLs are:
# GET /list -> {channel-ids: [INT..]} # GET /list -> {channelids: [INT..]}
# POST /allocate {side: SIDE} -> {channel-id: INT} # POST /allocate {side: SIDE} -> {channelid: INT}
# these return all messages (base64) for CID= : # these return all messages (base64) for CID= :
# POST /CID {side:, phase:, body:} -> {messages: [{phase:, body:}..]} # POST /CID {side:, phase:, body:} -> {messages: [{phase:, body:}..]}
# GET /CID (no-eventsource) -> {messages: [{phase:, body:}..]} # GET /CID (no-eventsource) -> {messages: [{phase:, body:}..]}
@ -57,22 +57,22 @@ class EventsProtocol:
# all JSON responses include a "welcome:{..}" key # all JSON responses include a "welcome:{..}" key
class Channel(resource.Resource): class Channel(resource.Resource):
def __init__(self, channel_id, relay, db, welcome): def __init__(self, channelid, relay, db, welcome):
resource.Resource.__init__(self) resource.Resource.__init__(self)
self.channel_id = channel_id self.channelid = channelid
self.relay = relay self.relay = relay
self.db = db self.db = db
self.welcome = welcome self.welcome = welcome
self.event_channels = set() # ep self.event_channels = set() # ep
self.putChild(b"deallocate", Deallocator(self.channel_id, self.relay)) self.putChild(b"deallocate", Deallocator(self.channelid, self.relay))
def get_messages(self, request): def get_messages(self, request):
request.setHeader(b"content-type", b"application/json; charset=utf-8") request.setHeader(b"content-type", b"application/json; charset=utf-8")
messages = [] messages = []
for row in self.db.execute("SELECT * FROM `messages`" for row in self.db.execute("SELECT * FROM `messages`"
" WHERE `channel_id`=?" " WHERE `channelid`=?"
" ORDER BY `when` ASC", " ORDER BY `when` ASC",
(self.channel_id,)).fetchall(): (self.channelid,)).fetchall():
messages.append({"phase": row["phase"], "body": row["body"]}) messages.append({"phase": row["phase"], "body": row["body"]})
data = {"welcome": self.welcome, "messages": messages} data = {"welcome": self.welcome, "messages": messages}
return (json.dumps(data)+"\n").encode("utf-8") return (json.dumps(data)+"\n").encode("utf-8")
@ -87,9 +87,9 @@ class Channel(resource.Resource):
request.notifyFinish().addErrback(lambda f: request.notifyFinish().addErrback(lambda f:
self.event_channels.discard(ep)) self.event_channels.discard(ep))
for row in self.db.execute("SELECT * FROM `messages`" for row in self.db.execute("SELECT * FROM `messages`"
" WHERE `channel_id`=?" " WHERE `channelid`=?"
" ORDER BY `when` ASC", " ORDER BY `when` ASC",
(self.channel_id,)).fetchall(): (self.channelid,)).fetchall():
data = json.dumps({"phase": row["phase"], "body": row["body"]}) data = json.dumps({"phase": row["phase"], "body": row["body"]})
ep.sendEvent(data) ep.sendEvent(data)
return server.NOT_DONE_YET return server.NOT_DONE_YET
@ -111,35 +111,35 @@ class Channel(resource.Resource):
body = data["body"] body = data["body"]
self.db.execute("INSERT INTO `messages`" self.db.execute("INSERT INTO `messages`"
" (`channel_id`, `side`, `phase`, `body`, `when`)" " (`channelid`, `side`, `phase`, `body`, `when`)"
" VALUES (?,?,?,?,?)", " VALUES (?,?,?,?,?)",
(self.channel_id, side, phase, body, time.time())) (self.channelid, side, phase, body, time.time()))
self.db.execute("INSERT INTO `allocations`" self.db.execute("INSERT INTO `allocations`"
" (`channel_id`, `side`)" " (`channelid`, `side`)"
" VALUES (?,?)", " VALUES (?,?)",
(self.channel_id, side)) (self.channelid, side))
self.db.commit() self.db.commit()
self.broadcast_message(phase, body) self.broadcast_message(phase, body)
return self.get_messages(request) return self.get_messages(request)
class Deallocator(resource.Resource): class Deallocator(resource.Resource):
def __init__(self, channel_id, relay): def __init__(self, channelid, relay):
self.channel_id = channel_id self.channelid = channelid
self.relay = relay self.relay = relay
def render_POST(self, request): def render_POST(self, request):
content = request.content.read() content = request.content.read()
data = json.loads(content.decode("utf-8")) data = json.loads(content.decode("utf-8"))
side = data["side"] side = data["side"]
deleted = self.relay.maybe_free_child(self.channel_id, side) deleted = self.relay.maybe_free_child(self.channelid, side)
resp = {"status": "waiting"} resp = {"status": "waiting"}
if deleted: if deleted:
resp = {"status": "deleted"} resp = {"status": "deleted"}
return json.dumps(resp).encode("utf-8") return json.dumps(resp).encode("utf-8")
def get_allocated(db): def get_allocated(db):
c = db.execute("SELECT DISTINCT `channel_id` FROM `allocations`") c = db.execute("SELECT DISTINCT `channelid` FROM `allocations`")
return set([row["channel_id"] for row in c.fetchall()]) return set([row["channelid"] for row in c.fetchall()])
class Allocator(resource.Resource): class Allocator(resource.Resource):
def __init__(self, db, welcome): def __init__(self, db, welcome):
@ -147,7 +147,7 @@ class Allocator(resource.Resource):
self.db = db self.db = db
self.welcome = welcome self.welcome = welcome
def allocate_channel_id(self): def allocate_channelid(self):
allocated = get_allocated(self.db) allocated = get_allocated(self.db)
for size in range(1,4): # stick to 1-999 for now for size in range(1,4): # stick to 1-999 for now
available = set() available = set()
@ -161,7 +161,7 @@ class Allocator(resource.Resource):
cid = random.randrange(1000, 1000*1000) cid = random.randrange(1000, 1000*1000)
if cid not in allocated: if cid not in allocated:
return cid return cid
raise ValueError("unable to find a free channel-id") raise ValueError("unable to find a free channelid")
def render_POST(self, request): def render_POST(self, request):
content = request.content.read() content = request.content.read()
@ -169,15 +169,15 @@ class Allocator(resource.Resource):
side = data["side"] side = data["side"]
if not isinstance(side, type(u"")): if not isinstance(side, type(u"")):
raise TypeError("side must be string, not '%s'" % type(side)) raise TypeError("side must be string, not '%s'" % type(side))
channel_id = self.allocate_channel_id() channelid = self.allocate_channelid()
self.db.execute("INSERT INTO `allocations` VALUES (?,?)", self.db.execute("INSERT INTO `allocations` VALUES (?,?)",
(channel_id, side)) (channelid, side))
self.db.commit() self.db.commit()
log.msg("allocated #%d, now have %d DB channels" % log.msg("allocated #%d, now have %d DB channels" %
(channel_id, len(get_allocated(self.db)))) (channelid, len(get_allocated(self.db))))
request.setHeader(b"content-type", b"application/json; charset=utf-8") request.setHeader(b"content-type", b"application/json; charset=utf-8")
data = {"welcome": self.welcome, data = {"welcome": self.welcome,
"channel-id": channel_id} "channelid": channelid}
return (json.dumps(data)+"\n").encode("utf-8") return (json.dumps(data)+"\n").encode("utf-8")
class ChannelList(resource.Resource): class ChannelList(resource.Resource):
@ -186,11 +186,11 @@ class ChannelList(resource.Resource):
self.db = db self.db = db
self.welcome = welcome self.welcome = welcome
def render_GET(self, request): def render_GET(self, request):
c = self.db.execute("SELECT DISTINCT `channel_id` FROM `allocations`") c = self.db.execute("SELECT DISTINCT `channelid` FROM `allocations`")
allocated = sorted(set([row["channel_id"] for row in c.fetchall()])) allocated = sorted(set([row["channelid"] for row in c.fetchall()]))
request.setHeader(b"content-type", b"application/json; charset=utf-8") request.setHeader(b"content-type", b"application/json; charset=utf-8")
data = {"welcome": self.welcome, data = {"welcome": self.welcome,
"channel-ids": allocated} "channelids": allocated}
return (json.dumps(data)+"\n").encode("utf-8") return (json.dumps(data)+"\n").encode("utf-8")
class Relay(resource.Resource, service.MultiService): class Relay(resource.Resource, service.MultiService):
@ -214,45 +214,45 @@ class Relay(resource.Resource, service.MultiService):
return resource.ErrorPage(http.BAD_REQUEST, return resource.ErrorPage(http.BAD_REQUEST,
"invalid channel id", "invalid channel id",
"invalid channel id") "invalid channel id")
channel_id = int(path) channelid = int(path)
if not channel_id in self.channels: if not channelid in self.channels:
log.msg("spawning #%d" % channel_id) log.msg("spawning #%d" % channelid)
self.channels[channel_id] = Channel(channel_id, self, self.db, self.channels[channelid] = Channel(channelid, self, self.db,
self.welcome) self.welcome)
return self.channels[channel_id] return self.channels[channelid]
def maybe_free_child(self, channel_id, side): def maybe_free_child(self, channelid, side):
self.db.execute("DELETE FROM `allocations`" self.db.execute("DELETE FROM `allocations`"
" WHERE `channel_id`=? AND `side`=?", " WHERE `channelid`=? AND `side`=?",
(channel_id, side)) (channelid, side))
self.db.commit() self.db.commit()
remaining = self.db.execute("SELECT COUNT(*) FROM `allocations`" remaining = self.db.execute("SELECT COUNT(*) FROM `allocations`"
" WHERE `channel_id`=?", " WHERE `channelid`=?",
(channel_id,)).fetchone()[0] (channelid,)).fetchone()[0]
if remaining: if remaining:
return False return False
self.free_child(channel_id) self.free_child(channelid)
return True return True
def free_child(self, channel_id): def free_child(self, channelid):
self.db.execute("DELETE FROM `allocations` WHERE `channel_id`=?", self.db.execute("DELETE FROM `allocations` WHERE `channelid`=?",
(channel_id,)) (channelid,))
self.db.execute("DELETE FROM `messages` WHERE `channel_id`=?", self.db.execute("DELETE FROM `messages` WHERE `channelid`=?",
(channel_id,)) (channelid,))
self.db.commit() self.db.commit()
if channel_id in self.channels: if channelid in self.channels:
self.channels.pop(channel_id) self.channels.pop(channelid)
log.msg("freed+killed #%d, now have %d DB channels, %d live" % log.msg("freed+killed #%d, now have %d DB channels, %d live" %
(channel_id, len(get_allocated(self.db)), len(self.channels))) (channelid, len(get_allocated(self.db)), len(self.channels)))
def prune_old_channels(self): def prune_old_channels(self):
old = time.time() - CHANNEL_EXPIRATION_TIME old = time.time() - CHANNEL_EXPIRATION_TIME
for channel_id in get_allocated(self.db): for channelid in get_allocated(self.db):
c = self.db.execute("SELECT `when` FROM `messages`" c = self.db.execute("SELECT `when` FROM `messages`"
" WHERE `channel_id`=?" " WHERE `channelid`=?"
" ORDER BY `when` DESC LIMIT 1", (channel_id,)) " ORDER BY `when` DESC LIMIT 1", (channelid,))
rows = c.fetchall() rows = c.fetchall()
if not rows or (rows[0]["when"] < old): if not rows or (rows[0]["when"] < old):
log.msg("expiring %d" % channel_id) log.msg("expiring %d" % channelid)
self.free_child(channel_id) self.free_child(channelid)

View File

@ -76,20 +76,20 @@ class API(ServerBase, unittest.TestCase):
d = self.get("list") d = self.get("list")
def _check_list_1(data): def _check_list_1(data):
self.check_welcome(data) self.check_welcome(data)
self.failUnlessEqual(data["channel-ids"], []) self.failUnlessEqual(data["channelids"], [])
d.addCallback(_check_list_1) d.addCallback(_check_list_1)
d.addCallback(lambda _: self.post("allocate", {"side": "abc"})) d.addCallback(lambda _: self.post("allocate", {"side": "abc"}))
def _allocated(data): def _allocated(data):
self.failUnlessEqual(set(data.keys()), self.failUnlessEqual(set(data.keys()),
set(["welcome", "channel-id"])) set(["welcome", "channelid"]))
self.failUnlessIsInstance(data["channel-id"], int) self.failUnlessIsInstance(data["channelid"], int)
self.cid = data["channel-id"] self.cid = data["channelid"]
d.addCallback(_allocated) d.addCallback(_allocated)
d.addCallback(lambda _: self.get("list")) d.addCallback(lambda _: self.get("list"))
def _check_list_2(data): def _check_list_2(data):
self.failUnlessEqual(data["channel-ids"], [self.cid]) self.failUnlessEqual(data["channelids"], [self.cid])
d.addCallback(_check_list_2) d.addCallback(_check_list_2)
d.addCallback(lambda _: self.post("%d/deallocate" % self.cid, d.addCallback(lambda _: self.post("%d/deallocate" % self.cid,
@ -100,7 +100,7 @@ class API(ServerBase, unittest.TestCase):
d.addCallback(lambda _: self.get("list")) d.addCallback(lambda _: self.get("list"))
def _check_list_3(data): def _check_list_3(data):
self.failUnlessEqual(data["channel-ids"], []) self.failUnlessEqual(data["channelids"], [])
d.addCallback(_check_list_3) d.addCallback(_check_list_3)
return d return d
@ -108,7 +108,7 @@ class API(ServerBase, unittest.TestCase):
def test_allocate_2(self): def test_allocate_2(self):
d = self.post("allocate", {"side": "abc"}) d = self.post("allocate", {"side": "abc"})
def _allocated(data): def _allocated(data):
self.cid = data["channel-id"] self.cid = data["channelid"]
d.addCallback(_allocated) d.addCallback(_allocated)
# second caller increases the number of known sides to 2 # second caller increases the number of known sides to 2
@ -119,7 +119,7 @@ class API(ServerBase, unittest.TestCase):
d.addCallback(lambda _: self.get("list")) d.addCallback(lambda _: self.get("list"))
d.addCallback(lambda data: d.addCallback(lambda data:
self.failUnlessEqual(data["channel-ids"], [self.cid])) self.failUnlessEqual(data["channelids"], [self.cid]))
d.addCallback(lambda _: self.post("%d/deallocate" % self.cid, d.addCallback(lambda _: self.post("%d/deallocate" % self.cid,
{"side": "abc"})) {"side": "abc"}))
@ -138,7 +138,7 @@ class API(ServerBase, unittest.TestCase):
d.addCallback(lambda _: self.get("list")) d.addCallback(lambda _: self.get("list"))
d.addCallback(lambda data: d.addCallback(lambda data:
self.failUnlessEqual(data["channel-ids"], [])) self.failUnlessEqual(data["channelids"], []))
return d return d
@ -166,7 +166,7 @@ class API(ServerBase, unittest.TestCase):
def test_messages(self): def test_messages(self):
d = self.post("allocate", {"side": "abc"}) d = self.post("allocate", {"side": "abc"})
def _allocated(data): def _allocated(data):
self.cid = data["channel-id"] self.cid = data["channelid"]
d.addCallback(_allocated) d.addCallback(_allocated)
d.addCallback(lambda _: self.add_message("msg1A")) d.addCallback(lambda _: self.add_message("msg1A"))
@ -211,7 +211,7 @@ class API(ServerBase, unittest.TestCase):
d = self.post("allocate", {"side": "abc"}) d = self.post("allocate", {"side": "abc"})
def _allocated(data): def _allocated(data):
self.cid = data["channel-id"] self.cid = data["channelid"]
url = (self.relayurl+str(self.cid)).encode("utf-8") url = (self.relayurl+str(self.cid)).encode("utf-8")
self.o = OneEventAtATime(url, parser=json.loads) self.o = OneEventAtATime(url, parser=json.loads)
return self.o.wait_for_connection() return self.o.wait_for_connection()

View File

@ -46,9 +46,9 @@ def post_json(agent, url, request_body):
return d return d
class Channel: class Channel:
def __init__(self, relay, channel_id, side, handle_welcome, def __init__(self, relay, channelid, side, handle_welcome,
agent): agent):
self._channel_url = "%s%d" % (relay, channel_id) self._channel_url = "%s%d" % (relay, channelid)
self._side = side self._side = side
self._handle_welcome = handle_welcome self._handle_welcome = handle_welcome
self._agent = agent self._agent = agent
@ -127,15 +127,15 @@ class ChannelManager:
def _got_channel(data): def _got_channel(data):
if "welcome" in data: if "welcome" in data:
self._handle_welcome(data["welcome"]) self._handle_welcome(data["welcome"])
return data["channel-id"] return data["channelid"]
d.addCallback(_got_channel) d.addCallback(_got_channel)
return d return d
def list_channels(self): def list_channels(self):
raise NotImplementedError raise NotImplementedError
def connect(self, channel_id): def connect(self, channelid):
return Channel(self._relay, channel_id, self._side, return Channel(self._relay, channelid, self._side,
self._handle_welcome, self._agent) self._handle_welcome, self._agent)
class Wormhole: class Wormhole:
@ -186,29 +186,29 @@ class Wormhole:
if self._started_get_code: raise UsageError if self._started_get_code: raise UsageError
self._started_get_code = True self._started_get_code = True
d = self._channel_manager.allocate() d = self._channel_manager.allocate()
def _got_channel_id(channel_id): def _got_channelid(channelid):
code = codes.make_code(channel_id, code_length) code = codes.make_code(channelid, code_length)
assert isinstance(code, str), type(code) assert isinstance(code, str), type(code)
self._set_code_and_channel_id(code) self._set_code_and_channelid(code)
self._start() self._start()
return code return code
d.addCallback(_got_channel_id) d.addCallback(_got_channelid)
return d return d
def set_code(self, code): def set_code(self, code):
if not isinstance(code, str): raise UsageError if not isinstance(code, str): raise UsageError
if self.code is not None: raise UsageError if self.code is not None: raise UsageError
self._set_code_and_channel_id(code) self._set_code_and_channelid(code)
self._start() self._start()
def _set_code_and_channel_id(self, code): def _set_code_and_channelid(self, code):
if self.code is not None: raise UsageError if self.code is not None: raise UsageError
mo = re.search(r'^(\d+)-', code) mo = re.search(r'^(\d+)-', code)
if not mo: if not mo:
raise ValueError("code (%s) must start with NN-" % code) raise ValueError("code (%s) must start with NN-" % code)
self.code = code self.code = code
channel_id = int(mo.group(1)) channelid = int(mo.group(1))
self.channel = self._channel_manager.connect(channel_id) self.channel = self._channel_manager.connect(channelid)
def _start(self): def _start(self):
# allocate the rest now too, so it can be serialized # allocate the rest now too, so it can be serialized
@ -238,7 +238,7 @@ class Wormhole:
d = json.loads(data) d = json.loads(data)
self = klass(d["appid"].encode("ascii"), d["relay"].encode("ascii")) self = klass(d["appid"].encode("ascii"), d["relay"].encode("ascii"))
self._set_side(d["side"].encode("ascii")) self._set_side(d["side"].encode("ascii"))
self._set_code_and_channel_id(d["code"].encode("ascii")) self._set_code_and_channelid(d["code"].encode("ascii"))
self.sp = SPAKE2_Symmetric.from_serialized(json.dumps(d["spake2"])) self.sp = SPAKE2_Symmetric.from_serialized(json.dumps(d["spake2"]))
self.msg1 = d["msg1"].decode("hex") self.msg1 = d["msg1"].decode("hex")
return self return self