appid and derive_key(purpose) are now unicode

This commit is contained in:
Brian Warner 2015-10-06 17:02:52 -07:00
parent 9ba7de6e1e
commit 9e1a00cbd9
8 changed files with 72 additions and 76 deletions

View File

@ -63,7 +63,7 @@ The synchronous+blocking flow looks like this:
from wormhole.blocking.transcribe import Wormhole from wormhole.blocking.transcribe import Wormhole
from wormhole.public_relay import RENDEZVOUS_RELAY from wormhole.public_relay import RENDEZVOUS_RELAY
mydata = b"initiator's data" mydata = b"initiator's data"
i = Wormhole(b"appid", RENDEZVOUS_RELAY) i = Wormhole(u"appid", RENDEZVOUS_RELAY)
code = i.get_code() code = i.get_code()
print("Invitation Code: %s" % code) print("Invitation Code: %s" % code)
i.send_data(mydata) i.send_data(mydata)
@ -78,7 +78,7 @@ from wormhole.blocking.transcribe import Wormhole
from wormhole.public_relay import RENDEZVOUS_RELAY from wormhole.public_relay import RENDEZVOUS_RELAY
mydata = b"receiver's data" mydata = b"receiver's data"
code = sys.argv[1] code = sys.argv[1]
r = Wormhole(b"appid", RENDEZVOUS_RELAY) r = Wormhole(u"appid", RENDEZVOUS_RELAY)
r.set_code(code) r.set_code(code)
r.send_data(mydata) r.send_data(mydata)
theirdata = r.get_data() theirdata = r.get_data()
@ -95,7 +95,7 @@ from twisted.internet import reactor
from wormhole.public_relay import RENDEZVOUS_RELAY from wormhole.public_relay import RENDEZVOUS_RELAY
from wormhole.twisted.transcribe import Wormhole from wormhole.twisted.transcribe import Wormhole
outbound_message = b"outbound data" outbound_message = b"outbound data"
w1 = Wormhole(b"appid", RENDEZVOUS_RELAY) w1 = Wormhole(u"appid", RENDEZVOUS_RELAY)
d = w1.get_code() d = w1.get_code()
def _got_code(code): def _got_code(code):
print "Invitation Code:", code print "Invitation Code:", code
@ -113,7 +113,7 @@ reactor.run()
On the other side, you call `set_code()` instead of waiting for `get_code()`: On the other side, you call `set_code()` instead of waiting for `get_code()`:
```python ```python
w2 = Wormhole(b"appid", RENDEZVOUS_RELAY) w2 = Wormhole(u"appid", RENDEZVOUS_RELAY)
w2.set_code(code) w2.set_code(code)
d = w2.send_data(my_message) d = w2.send_data(my_message)
... ...
@ -163,12 +163,12 @@ unicode in python3, plain bytes in python2).
## Application Identifier ## Application Identifier
Applications using this library must provide an "application identifier", a Applications using this library must provide an "application identifier", a
simple bytestring that distinguishes one application from another. To ensure simple string that distinguishes one application from another. To ensure
uniqueness, use a domain name. To use multiple apps for a single domain, just uniqueness, use a domain name. To use multiple apps for a single domain,
use a string like `example.com/app1`. This string must be the same on both append a URL-like slash and path, like `example.com/app1`. This string must
clients, otherwise they will not see each other. The invitation codes are be the same on both clients, otherwise they will not see each other. The
scoped to the app-id. Note that the app-id must be a bytestring, not unicode, invitation codes are scoped to the app-id. Note that the app-id must be
so on python3 use `b"appid"`. unicode, not bytes, so on python2 use `u"appid"`.
Distinct app-ids reduce the size of the connection-id numbers. If fewer than Distinct app-ids reduce the size of the connection-id numbers. If fewer than
ten initiators are active for a given app-id, the connection-id will only ten initiators are active for a given app-id, the connection-id will only
@ -244,10 +244,8 @@ re-send it if necessary.
All cryptographically-sensitive parameters are passed as bytes ("str" in All cryptographically-sensitive parameters are passed as bytes ("str" in
python2, "bytes" in python3): python2, "bytes" in python3):
* application identifier
* verifier string * verifier string
* data in/out * data in/out
* derived-key "purpose" string
* transit records in/out * transit records in/out
Some human-readable parameters are passed as strings: "str" in python2, "str" Some human-readable parameters are passed as strings: "str" in python2, "str"
@ -260,6 +258,8 @@ Some human-readable parameters are passed as strings: "str" in python2, "str"
And some are always unicode, in both python2 and python3: And some are always unicode, in both python2 and python3:
* relay URL * relay URL
* application identifier
* derived-key "purpose" string: `w.derive_key(PURPOSE)`
## Detailed Example ## Detailed Example

View File

@ -1,5 +1,5 @@
from __future__ import print_function from __future__ import print_function
import os, sys, time, re, requests, json import os, sys, time, re, requests, json, unicodedata
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from spake2 import SPAKE2_Symmetric from spake2 import SPAKE2_Symmetric
from nacl.secret import SecretBox from nacl.secret import SecretBox
@ -15,6 +15,9 @@ from ..channel_monitor import monitor
SECOND = 1 SECOND = 1
MINUTE = 60*SECOND MINUTE = 60*SECOND
def to_bytes(u):
return unicodedata.normalize("NFC", u).encode("utf-8")
# relay URLs are: # relay URLs are:
# GET /list -> {channelids: [INT..]} # GET /list -> {channelids: [INT..]}
# POST /allocate {side: SIDE} -> {channelid: INT} # POST /allocate {side: SIDE} -> {channelid: INT}
@ -130,7 +133,7 @@ class Wormhole:
version_warning_displayed = False version_warning_displayed = False
def __init__(self, appid, relay_url): def __init__(self, appid, relay_url):
if not isinstance(appid, type(b"")): raise UsageError if not isinstance(appid, type(u"")): raise UsageError
if not isinstance(relay_url, type(u"")): raise UsageError if not isinstance(relay_url, type(u"")): raise UsageError
if not relay_url.endswith(u"/"): raise UsageError if not relay_url.endswith(u"/"): raise UsageError
self._appid = appid self._appid = appid
@ -201,12 +204,12 @@ class Wormhole:
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
self.sp = SPAKE2_Symmetric(self.code.encode("ascii"), self.sp = SPAKE2_Symmetric(self.code.encode("ascii"),
idSymmetric=self._appid) idSymmetric=to_bytes(self._appid))
self.msg1 = self.sp.start() self.msg1 = self.sp.start()
def derive_key(self, purpose, length=SecretBox.KEY_SIZE): def derive_key(self, purpose, length=SecretBox.KEY_SIZE):
if not isinstance(purpose, type(b"")): raise UsageError if not isinstance(purpose, type(u"")): raise UsageError
return HKDF(self.key, length, CTXinfo=purpose) return HKDF(self.key, length, CTXinfo=to_bytes(purpose))
def _encrypt_data(self, key, data): def _encrypt_data(self, key, data):
assert isinstance(key, type(b"")), type(key) assert isinstance(key, type(b"")), type(key)
@ -230,7 +233,7 @@ class Wormhole:
self.channel.send(u"pake", self.msg1) self.channel.send(u"pake", self.msg1)
pake_msg = self.channel.get(u"pake") pake_msg = self.channel.get(u"pake")
self.key = self.sp.finish(pake_msg) self.key = self.sp.finish(pake_msg)
self.verifier = self.derive_key(self._appid+b":Verifier") self.verifier = self.derive_key(self._appid+u":Verifier")
def get_verifier(self): def get_verifier(self):
if self.code is None: raise UsageError if self.code is None: raise UsageError
@ -248,7 +251,7 @@ class Wormhole:
# nonces to keep the messages distinct, and the Channel automatically # nonces to keep the messages distinct, and the Channel automatically
# ignores reflections. # ignores reflections.
self._get_key() self._get_key()
data_key = self.derive_key(b"data-key") data_key = self.derive_key(u"data-key")
outbound_encrypted = self._encrypt_data(data_key, outbound_data) outbound_encrypted = self._encrypt_data(data_key, outbound_data)
self.channel.send(u"data", outbound_encrypted) self.channel.send(u"data", outbound_encrypted)
@ -257,7 +260,7 @@ class Wormhole:
if self.code is None: raise UsageError if self.code is None: raise UsageError
if self.channel is None: raise UsageError if self.channel is None: raise UsageError
self._get_key() self._get_key()
data_key = self.derive_key(b"data-key") data_key = self.derive_key(u"data-key")
inbound_encrypted = self.channel.get(u"data") inbound_encrypted = self.channel.get(u"data")
try: try:
inbound_data = self._decrypt_data(data_key, inbound_encrypted) inbound_data = self._decrypt_data(data_key, inbound_encrypted)

View File

@ -2,7 +2,7 @@ from __future__ import print_function
import os, sys, json, binascii, six import os, sys, json, binascii, six
from ..errors import handle_server_error from ..errors import handle_server_error
APPID = b"lothar.com/wormhole/text-or-file-xfer" APPID = u"lothar.com/wormhole/text-or-file-xfer"
@handle_server_error @handle_server_error
def receive(args): def receive(args):
@ -94,7 +94,7 @@ def receive(args):
# now receive the rest of the owl # now receive the rest of the owl
tdata = them_d["transit"] tdata = them_d["transit"]
transit_key = w.derive_key(APPID+b"/transit-key") transit_key = w.derive_key(APPID+u"/transit-key")
transit_receiver.set_transit_key(transit_key) transit_receiver.set_transit_key(transit_key)
transit_receiver.add_their_direct_hints(tdata["direct_connection_hints"]) transit_receiver.add_their_direct_hints(tdata["direct_connection_hints"])
transit_receiver.add_their_relay_hints(tdata["relay_connection_hints"]) transit_receiver.add_their_relay_hints(tdata["relay_connection_hints"])

View File

@ -2,7 +2,7 @@ from __future__ import print_function
import os, sys, json, binascii, six import os, sys, json, binascii, six
from ..errors import handle_server_error from ..errors import handle_server_error
APPID = b"lothar.com/wormhole/text-or-file-xfer" APPID = u"lothar.com/wormhole/text-or-file-xfer"
@handle_server_error @handle_server_error
def send(args): def send(args):
@ -109,7 +109,7 @@ def send(args):
w.close() w.close()
tdata = them_phase1["transit"] tdata = them_phase1["transit"]
transit_key = w.derive_key(APPID+b"/transit-key") transit_key = w.derive_key(APPID+"/transit-key")
transit_sender.set_transit_key(transit_key) transit_sender.set_transit_key(transit_key)
transit_sender.add_their_direct_hints(tdata["direct_connection_hints"]) transit_sender.add_their_direct_hints(tdata["direct_connection_hints"])
transit_sender.add_their_relay_hints(tdata["relay_connection_hints"]) transit_sender.add_their_relay_hints(tdata["relay_connection_hints"])

View File

@ -5,6 +5,8 @@ from twisted.internet.threads import deferToThread
from ..blocking.transcribe import Wormhole as BlockingWormhole, UsageError from ..blocking.transcribe import Wormhole as BlockingWormhole, UsageError
from .common import ServerBase from .common import ServerBase
APPID = u"appid"
class Blocking(ServerBase, unittest.TestCase): class Blocking(ServerBase, unittest.TestCase):
# we need Twisted to run the server, but we run the sender and receiver # we need Twisted to run the server, but we run the sender and receiver
# with deferToThread() # with deferToThread()
@ -18,9 +20,8 @@ class Blocking(ServerBase, unittest.TestCase):
deferToThread(f2, *f2args)], True) deferToThread(f2, *f2args)], True)
def test_basic(self): def test_basic(self):
appid = b"appid" w1 = BlockingWormhole(APPID, self.relayurl)
w1 = BlockingWormhole(appid, self.relayurl) w2 = BlockingWormhole(APPID, self.relayurl)
w2 = BlockingWormhole(appid, self.relayurl)
d = deferToThread(w1.get_code) d = deferToThread(w1.get_code)
def _got_code(code): def _got_code(code):
w2.set_code(code) w2.set_code(code)
@ -39,9 +40,8 @@ class Blocking(ServerBase, unittest.TestCase):
return d return d
def test_interleaved(self): def test_interleaved(self):
appid = b"appid" w1 = BlockingWormhole(APPID, self.relayurl)
w1 = BlockingWormhole(appid, self.relayurl) w2 = BlockingWormhole(APPID, self.relayurl)
w2 = BlockingWormhole(appid, self.relayurl)
d = deferToThread(w1.get_code) d = deferToThread(w1.get_code)
def _got_code(code): def _got_code(code):
w2.set_code(code) w2.set_code(code)
@ -61,9 +61,8 @@ class Blocking(ServerBase, unittest.TestCase):
return d return d
def test_fixed_code(self): def test_fixed_code(self):
appid = b"appid" w1 = BlockingWormhole(APPID, self.relayurl)
w1 = BlockingWormhole(appid, self.relayurl) w2 = BlockingWormhole(APPID, self.relayurl)
w2 = BlockingWormhole(appid, self.relayurl)
w1.set_code("123-purple-elephant") w1.set_code("123-purple-elephant")
w2.set_code("123-purple-elephant") w2.set_code("123-purple-elephant")
d = self.doBoth([w1.send_data, b"data1"], [w2.send_data, b"data2"]) d = self.doBoth([w1.send_data, b"data1"], [w2.send_data, b"data2"])
@ -79,9 +78,8 @@ class Blocking(ServerBase, unittest.TestCase):
return d return d
def test_verifier(self): def test_verifier(self):
appid = b"appid" w1 = BlockingWormhole(APPID, self.relayurl)
w1 = BlockingWormhole(appid, self.relayurl) w2 = BlockingWormhole(APPID, self.relayurl)
w2 = BlockingWormhole(appid, self.relayurl)
d = deferToThread(w1.get_code) d = deferToThread(w1.get_code)
def _got_code(code): def _got_code(code):
w2.set_code(code) w2.set_code(code)
@ -106,9 +104,8 @@ class Blocking(ServerBase, unittest.TestCase):
return d return d
def test_verifier_mismatch(self): def test_verifier_mismatch(self):
appid = b"appid" w1 = BlockingWormhole(APPID, self.relayurl)
w1 = BlockingWormhole(appid, self.relayurl) w2 = BlockingWormhole(APPID, self.relayurl)
w2 = BlockingWormhole(appid, self.relayurl)
d = deferToThread(w1.get_code) d = deferToThread(w1.get_code)
def _got_code(code): def _got_code(code):
w2.set_code(code+"not") w2.set_code(code+"not")
@ -123,15 +120,14 @@ class Blocking(ServerBase, unittest.TestCase):
return d return d
def test_errors(self): def test_errors(self):
appid = b"appid" w1 = BlockingWormhole(APPID, self.relayurl)
w1 = BlockingWormhole(appid, self.relayurl)
self.assertRaises(UsageError, w1.get_verifier) self.assertRaises(UsageError, w1.get_verifier)
self.assertRaises(UsageError, w1.get_data) self.assertRaises(UsageError, w1.get_data)
self.assertRaises(UsageError, w1.send_data, b"data") self.assertRaises(UsageError, w1.send_data, b"data")
w1.set_code("123-purple-elephant") w1.set_code("123-purple-elephant")
self.assertRaises(UsageError, w1.set_code, "123-nope") self.assertRaises(UsageError, w1.set_code, "123-nope")
self.assertRaises(UsageError, w1.get_code) self.assertRaises(UsageError, w1.get_code)
w2 = BlockingWormhole(appid, self.relayurl) w2 = BlockingWormhole(APPID, self.relayurl)
d = deferToThread(w2.get_code) d = deferToThread(w2.get_code)
def _done(code): def _done(code):
self.assertRaises(UsageError, w2.get_code) self.assertRaises(UsageError, w2.get_code)
@ -140,10 +136,9 @@ class Blocking(ServerBase, unittest.TestCase):
return d return d
def test_serialize(self): def test_serialize(self):
appid = b"appid" w1 = BlockingWormhole(APPID, self.relayurl)
w1 = BlockingWormhole(appid, self.relayurl)
self.assertRaises(UsageError, w1.serialize) # too early self.assertRaises(UsageError, w1.serialize) # too early
w2 = BlockingWormhole(appid, self.relayurl) w2 = BlockingWormhole(APPID, self.relayurl)
d = deferToThread(w1.get_code) d = deferToThread(w1.get_code)
def _got_code(code): def _got_code(code):
self.assertRaises(UsageError, w2.serialize) # too early self.assertRaises(UsageError, w2.serialize) # too early

View File

@ -4,15 +4,16 @@ from twisted.internet.defer import gatherResults
from ..twisted.transcribe import Wormhole, UsageError from ..twisted.transcribe import Wormhole, UsageError
from .common import ServerBase from .common import ServerBase
APPID = u"appid"
class Basic(ServerBase, unittest.TestCase): class Basic(ServerBase, unittest.TestCase):
def doBoth(self, d1, d2): def doBoth(self, d1, d2):
return gatherResults([d1, d2], True) return gatherResults([d1, d2], True)
def test_basic(self): def test_basic(self):
appid = b"appid" w1 = Wormhole(APPID, self.relayurl)
w1 = Wormhole(appid, self.relayurl) w2 = Wormhole(APPID, self.relayurl)
w2 = Wormhole(appid, self.relayurl)
d = w1.get_code() d = w1.get_code()
def _got_code(code): def _got_code(code):
w2.set_code(code) w2.set_code(code)
@ -30,9 +31,8 @@ class Basic(ServerBase, unittest.TestCase):
return d return d
def test_interleaved(self): def test_interleaved(self):
appid = b"appid" w1 = Wormhole(APPID, self.relayurl)
w1 = Wormhole(appid, self.relayurl) w2 = Wormhole(APPID, self.relayurl)
w2 = Wormhole(appid, self.relayurl)
d = w1.get_code() d = w1.get_code()
def _got_code(code): def _got_code(code):
w2.set_code(code) w2.set_code(code)
@ -51,9 +51,8 @@ class Basic(ServerBase, unittest.TestCase):
return d return d
def test_fixed_code(self): def test_fixed_code(self):
appid = b"appid" w1 = Wormhole(APPID, self.relayurl)
w1 = Wormhole(appid, self.relayurl) w2 = Wormhole(APPID, self.relayurl)
w2 = Wormhole(appid, self.relayurl)
w1.set_code("123-purple-elephant") w1.set_code("123-purple-elephant")
w2.set_code("123-purple-elephant") w2.set_code("123-purple-elephant")
d = self.doBoth(w1.send_data(b"data1"), w2.send_data(b"data2")) d = self.doBoth(w1.send_data(b"data1"), w2.send_data(b"data2"))
@ -69,9 +68,8 @@ class Basic(ServerBase, unittest.TestCase):
return d return d
def test_verifier(self): def test_verifier(self):
appid = b"appid" w1 = Wormhole(APPID, self.relayurl)
w1 = Wormhole(appid, self.relayurl) w2 = Wormhole(APPID, self.relayurl)
w2 = Wormhole(appid, self.relayurl)
d = w1.get_code() d = w1.get_code()
def _got_code(code): def _got_code(code):
w2.set_code(code) w2.set_code(code)
@ -95,9 +93,8 @@ class Basic(ServerBase, unittest.TestCase):
return d return d
def test_verifier_mismatch(self): def test_verifier_mismatch(self):
appid = b"appid" w1 = Wormhole(APPID, self.relayurl)
w1 = Wormhole(appid, self.relayurl) w2 = Wormhole(APPID, self.relayurl)
w2 = Wormhole(appid, self.relayurl)
d = w1.get_code() d = w1.get_code()
def _got_code(code): def _got_code(code):
w2.set_code(code+"not") w2.set_code(code+"not")
@ -112,15 +109,14 @@ class Basic(ServerBase, unittest.TestCase):
return d return d
def test_errors(self): def test_errors(self):
appid = b"appid" w1 = Wormhole(APPID, self.relayurl)
w1 = Wormhole(appid, self.relayurl)
self.assertRaises(UsageError, w1.get_verifier) self.assertRaises(UsageError, w1.get_verifier)
self.assertRaises(UsageError, w1.send_data, b"data") self.assertRaises(UsageError, w1.send_data, b"data")
self.assertRaises(UsageError, w1.get_data) self.assertRaises(UsageError, w1.get_data)
w1.set_code("123-purple-elephant") w1.set_code("123-purple-elephant")
self.assertRaises(UsageError, w1.set_code, "123-nope") self.assertRaises(UsageError, w1.set_code, "123-nope")
self.assertRaises(UsageError, w1.get_code) self.assertRaises(UsageError, w1.get_code)
w2 = Wormhole(appid, self.relayurl) w2 = Wormhole(APPID, self.relayurl)
d = w2.get_code() d = w2.get_code()
self.assertRaises(UsageError, w2.get_code) self.assertRaises(UsageError, w2.get_code)
def _got_code(code): def _got_code(code):
@ -129,10 +125,9 @@ class Basic(ServerBase, unittest.TestCase):
return d return d
def test_serialize(self): def test_serialize(self):
appid = b"appid" w1 = Wormhole(APPID, self.relayurl)
w1 = Wormhole(appid, self.relayurl)
self.assertRaises(UsageError, w1.serialize) # too early self.assertRaises(UsageError, w1.serialize) # too early
w2 = Wormhole(appid, self.relayurl) w2 = Wormhole(APPID, self.relayurl)
d = w1.get_code() d = w1.get_code()
def _got_code(code): def _got_code(code):
self.assertRaises(UsageError, w2.serialize) # too early self.assertRaises(UsageError, w2.serialize) # too early

View File

@ -5,7 +5,7 @@ from twisted.internet import reactor
from .transcribe import Wormhole from .transcribe import Wormhole
from .. import public_relay from .. import public_relay
APPID = b"lothar.com/wormhole/text-or-file-xfer" APPID = u"lothar.com/wormhole/text-or-file-xfer"
w = Wormhole(APPID, public_relay.RENDEZVOUS_RELAY) w = Wormhole(APPID, public_relay.RENDEZVOUS_RELAY)

View File

@ -1,5 +1,5 @@
from __future__ import print_function from __future__ import print_function
import os, sys, json, re import os, sys, json, re, unicodedata
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from zope.interface import implementer from zope.interface import implementer
from twisted.internet import reactor, defer from twisted.internet import reactor, defer
@ -17,6 +17,9 @@ from ..errors import ServerError, WrongPasswordError, UsageError
from ..util.hkdf import HKDF from ..util.hkdf import HKDF
from ..channel_monitor import monitor from ..channel_monitor import monitor
def to_bytes(u):
return unicodedata.normalize("NFC", u).encode("utf-8")
@implementer(IBodyProducer) @implementer(IBodyProducer)
class DataProducer: class DataProducer:
def __init__(self, data): def __init__(self, data):
@ -146,7 +149,7 @@ class Wormhole:
version_warning_displayed = False version_warning_displayed = False
def __init__(self, appid, relay_url): def __init__(self, appid, relay_url):
if not isinstance(appid, type(b"")): raise UsageError if not isinstance(appid, type(u"")): raise UsageError
if not isinstance(relay_url, type(u"")): raise UsageError if not isinstance(relay_url, type(u"")): raise UsageError
if not relay_url.endswith(u"/"): raise UsageError if not relay_url.endswith(u"/"): raise UsageError
self._appid = appid self._appid = appid
@ -219,7 +222,7 @@ class Wormhole:
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
self.sp = SPAKE2_Symmetric(self.code.encode("ascii"), self.sp = SPAKE2_Symmetric(self.code.encode("ascii"),
idSymmetric=self._appid) idSymmetric=to_bytes(self._appid))
self.msg1 = self.sp.start() self.msg1 = self.sp.start()
def serialize(self): def serialize(self):
@ -242,7 +245,7 @@ class Wormhole:
@classmethod @classmethod
def from_serialized(klass, data): def from_serialized(klass, data):
d = json.loads(data) d = json.loads(data)
self = klass(d["appid"].encode("ascii"), d["relay_url"]) self = klass(d["appid"], d["relay_url"])
self._set_side(d["side"].encode("ascii")) self._set_side(d["side"].encode("ascii"))
self._set_code_and_channelid(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"]))
@ -250,11 +253,11 @@ class Wormhole:
return self return self
def derive_key(self, purpose, length=SecretBox.KEY_SIZE): def derive_key(self, purpose, length=SecretBox.KEY_SIZE):
if not isinstance(purpose, type(u"")): raise UsageError
if self.key is None: if self.key is None:
# call after get_verifier() or get_data() # call after get_verifier() or get_data()
raise UsageError raise UsageError
if not isinstance(purpose, type(b"")): raise UsageError return HKDF(self.key, length, CTXinfo=to_bytes(purpose))
return HKDF(self.key, length, CTXinfo=purpose)
def _encrypt_data(self, key, data): def _encrypt_data(self, key, data):
assert isinstance(key, type(b"")), type(key) assert isinstance(key, type(b"")), type(key)
@ -282,7 +285,7 @@ class Wormhole:
def _got_pake(pake_msg): def _got_pake(pake_msg):
key = self.sp.finish(pake_msg) key = self.sp.finish(pake_msg)
self.key = key self.key = key
self.verifier = self.derive_key(self._appid+b":Verifier") self.verifier = self.derive_key(self._appid+u":Verifier")
return key return key
d.addCallback(_got_pake) d.addCallback(_got_pake)
return d return d
@ -304,7 +307,7 @@ class Wormhole:
# ignores reflections. # ignores reflections.
d = self._get_key() d = self._get_key()
def _send(key): def _send(key):
data_key = self.derive_key(b"data-key") data_key = self.derive_key(u"data-key")
outbound_encrypted = self._encrypt_data(data_key, outbound_data) outbound_encrypted = self._encrypt_data(data_key, outbound_data)
return self.channel.send(u"data", outbound_encrypted) return self.channel.send(u"data", outbound_encrypted)
d.addCallback(_send) d.addCallback(_send)
@ -316,7 +319,7 @@ class Wormhole:
if self.channel is None: raise UsageError if self.channel is None: raise UsageError
d = self._get_key() d = self._get_key()
def _get(key): def _get(key):
data_key = self.derive_key(b"data-key") data_key = self.derive_key(u"data-key")
d1 = self.channel.get(u"data") d1 = self.channel.get(u"data")
def _decrypt(inbound_encrypted): def _decrypt(inbound_encrypted):
try: try: