appid and derive_key(purpose) are now unicode
This commit is contained in:
parent
9ba7de6e1e
commit
9e1a00cbd9
24
docs/api.md
24
docs/api.md
|
@ -63,7 +63,7 @@ The synchronous+blocking flow looks like this:
|
|||
from wormhole.blocking.transcribe import Wormhole
|
||||
from wormhole.public_relay import RENDEZVOUS_RELAY
|
||||
mydata = b"initiator's data"
|
||||
i = Wormhole(b"appid", RENDEZVOUS_RELAY)
|
||||
i = Wormhole(u"appid", RENDEZVOUS_RELAY)
|
||||
code = i.get_code()
|
||||
print("Invitation Code: %s" % code)
|
||||
i.send_data(mydata)
|
||||
|
@ -78,7 +78,7 @@ from wormhole.blocking.transcribe import Wormhole
|
|||
from wormhole.public_relay import RENDEZVOUS_RELAY
|
||||
mydata = b"receiver's data"
|
||||
code = sys.argv[1]
|
||||
r = Wormhole(b"appid", RENDEZVOUS_RELAY)
|
||||
r = Wormhole(u"appid", RENDEZVOUS_RELAY)
|
||||
r.set_code(code)
|
||||
r.send_data(mydata)
|
||||
theirdata = r.get_data()
|
||||
|
@ -95,7 +95,7 @@ from twisted.internet import reactor
|
|||
from wormhole.public_relay import RENDEZVOUS_RELAY
|
||||
from wormhole.twisted.transcribe import Wormhole
|
||||
outbound_message = b"outbound data"
|
||||
w1 = Wormhole(b"appid", RENDEZVOUS_RELAY)
|
||||
w1 = Wormhole(u"appid", RENDEZVOUS_RELAY)
|
||||
d = w1.get_code()
|
||||
def _got_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()`:
|
||||
|
||||
```python
|
||||
w2 = Wormhole(b"appid", RENDEZVOUS_RELAY)
|
||||
w2 = Wormhole(u"appid", RENDEZVOUS_RELAY)
|
||||
w2.set_code(code)
|
||||
d = w2.send_data(my_message)
|
||||
...
|
||||
|
@ -163,12 +163,12 @@ unicode in python3, plain bytes in python2).
|
|||
## Application Identifier
|
||||
|
||||
Applications using this library must provide an "application identifier", a
|
||||
simple bytestring that distinguishes one application from another. To ensure
|
||||
uniqueness, use a domain name. To use multiple apps for a single domain, just
|
||||
use a string like `example.com/app1`. This string must be the same on both
|
||||
clients, otherwise they will not see each other. The invitation codes are
|
||||
scoped to the app-id. Note that the app-id must be a bytestring, not unicode,
|
||||
so on python3 use `b"appid"`.
|
||||
simple string that distinguishes one application from another. To ensure
|
||||
uniqueness, use a domain name. To use multiple apps for a single domain,
|
||||
append a URL-like slash and path, like `example.com/app1`. This string must
|
||||
be the same on both clients, otherwise they will not see each other. The
|
||||
invitation codes are scoped to the app-id. Note that the app-id must be
|
||||
unicode, not bytes, so on python2 use `u"appid"`.
|
||||
|
||||
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
|
||||
|
@ -244,10 +244,8 @@ re-send it if necessary.
|
|||
All cryptographically-sensitive parameters are passed as bytes ("str" in
|
||||
python2, "bytes" in python3):
|
||||
|
||||
* application identifier
|
||||
* verifier string
|
||||
* data in/out
|
||||
* derived-key "purpose" string
|
||||
* transit records in/out
|
||||
|
||||
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:
|
||||
|
||||
* relay URL
|
||||
* application identifier
|
||||
* derived-key "purpose" string: `w.derive_key(PURPOSE)`
|
||||
|
||||
## Detailed Example
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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 spake2 import SPAKE2_Symmetric
|
||||
from nacl.secret import SecretBox
|
||||
|
@ -15,6 +15,9 @@ from ..channel_monitor import monitor
|
|||
SECOND = 1
|
||||
MINUTE = 60*SECOND
|
||||
|
||||
def to_bytes(u):
|
||||
return unicodedata.normalize("NFC", u).encode("utf-8")
|
||||
|
||||
# relay URLs are:
|
||||
# GET /list -> {channelids: [INT..]}
|
||||
# POST /allocate {side: SIDE} -> {channelid: INT}
|
||||
|
@ -130,7 +133,7 @@ class Wormhole:
|
|||
version_warning_displayed = False
|
||||
|
||||
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 relay_url.endswith(u"/"): raise UsageError
|
||||
self._appid = appid
|
||||
|
@ -201,12 +204,12 @@ class Wormhole:
|
|||
def _start(self):
|
||||
# allocate the rest now too, so it can be serialized
|
||||
self.sp = SPAKE2_Symmetric(self.code.encode("ascii"),
|
||||
idSymmetric=self._appid)
|
||||
idSymmetric=to_bytes(self._appid))
|
||||
self.msg1 = self.sp.start()
|
||||
|
||||
def derive_key(self, purpose, length=SecretBox.KEY_SIZE):
|
||||
if not isinstance(purpose, type(b"")): raise UsageError
|
||||
return HKDF(self.key, length, CTXinfo=purpose)
|
||||
if not isinstance(purpose, type(u"")): raise UsageError
|
||||
return HKDF(self.key, length, CTXinfo=to_bytes(purpose))
|
||||
|
||||
def _encrypt_data(self, key, data):
|
||||
assert isinstance(key, type(b"")), type(key)
|
||||
|
@ -230,7 +233,7 @@ class Wormhole:
|
|||
self.channel.send(u"pake", self.msg1)
|
||||
pake_msg = self.channel.get(u"pake")
|
||||
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):
|
||||
if self.code is None: raise UsageError
|
||||
|
@ -248,7 +251,7 @@ class Wormhole:
|
|||
# nonces to keep the messages distinct, and the Channel automatically
|
||||
# ignores reflections.
|
||||
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)
|
||||
self.channel.send(u"data", outbound_encrypted)
|
||||
|
||||
|
@ -257,7 +260,7 @@ class Wormhole:
|
|||
if self.code is None: raise UsageError
|
||||
if self.channel is None: raise UsageError
|
||||
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")
|
||||
try:
|
||||
inbound_data = self._decrypt_data(data_key, inbound_encrypted)
|
||||
|
|
|
@ -2,7 +2,7 @@ from __future__ import print_function
|
|||
import os, sys, json, binascii, six
|
||||
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
|
||||
def receive(args):
|
||||
|
@ -94,7 +94,7 @@ def receive(args):
|
|||
|
||||
# now receive the rest of the owl
|
||||
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.add_their_direct_hints(tdata["direct_connection_hints"])
|
||||
transit_receiver.add_their_relay_hints(tdata["relay_connection_hints"])
|
||||
|
|
|
@ -2,7 +2,7 @@ from __future__ import print_function
|
|||
import os, sys, json, binascii, six
|
||||
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
|
||||
def send(args):
|
||||
|
@ -109,7 +109,7 @@ def send(args):
|
|||
w.close()
|
||||
|
||||
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.add_their_direct_hints(tdata["direct_connection_hints"])
|
||||
transit_sender.add_their_relay_hints(tdata["relay_connection_hints"])
|
||||
|
|
|
@ -5,6 +5,8 @@ from twisted.internet.threads import deferToThread
|
|||
from ..blocking.transcribe import Wormhole as BlockingWormhole, UsageError
|
||||
from .common import ServerBase
|
||||
|
||||
APPID = u"appid"
|
||||
|
||||
class Blocking(ServerBase, unittest.TestCase):
|
||||
# we need Twisted to run the server, but we run the sender and receiver
|
||||
# with deferToThread()
|
||||
|
@ -18,9 +20,8 @@ class Blocking(ServerBase, unittest.TestCase):
|
|||
deferToThread(f2, *f2args)], True)
|
||||
|
||||
def test_basic(self):
|
||||
appid = b"appid"
|
||||
w1 = BlockingWormhole(appid, self.relayurl)
|
||||
w2 = BlockingWormhole(appid, self.relayurl)
|
||||
w1 = BlockingWormhole(APPID, self.relayurl)
|
||||
w2 = BlockingWormhole(APPID, self.relayurl)
|
||||
d = deferToThread(w1.get_code)
|
||||
def _got_code(code):
|
||||
w2.set_code(code)
|
||||
|
@ -39,9 +40,8 @@ class Blocking(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_interleaved(self):
|
||||
appid = b"appid"
|
||||
w1 = BlockingWormhole(appid, self.relayurl)
|
||||
w2 = BlockingWormhole(appid, self.relayurl)
|
||||
w1 = BlockingWormhole(APPID, self.relayurl)
|
||||
w2 = BlockingWormhole(APPID, self.relayurl)
|
||||
d = deferToThread(w1.get_code)
|
||||
def _got_code(code):
|
||||
w2.set_code(code)
|
||||
|
@ -61,9 +61,8 @@ class Blocking(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_fixed_code(self):
|
||||
appid = b"appid"
|
||||
w1 = BlockingWormhole(appid, self.relayurl)
|
||||
w2 = BlockingWormhole(appid, self.relayurl)
|
||||
w1 = BlockingWormhole(APPID, self.relayurl)
|
||||
w2 = BlockingWormhole(APPID, self.relayurl)
|
||||
w1.set_code("123-purple-elephant")
|
||||
w2.set_code("123-purple-elephant")
|
||||
d = self.doBoth([w1.send_data, b"data1"], [w2.send_data, b"data2"])
|
||||
|
@ -79,9 +78,8 @@ class Blocking(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_verifier(self):
|
||||
appid = b"appid"
|
||||
w1 = BlockingWormhole(appid, self.relayurl)
|
||||
w2 = BlockingWormhole(appid, self.relayurl)
|
||||
w1 = BlockingWormhole(APPID, self.relayurl)
|
||||
w2 = BlockingWormhole(APPID, self.relayurl)
|
||||
d = deferToThread(w1.get_code)
|
||||
def _got_code(code):
|
||||
w2.set_code(code)
|
||||
|
@ -106,9 +104,8 @@ class Blocking(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_verifier_mismatch(self):
|
||||
appid = b"appid"
|
||||
w1 = BlockingWormhole(appid, self.relayurl)
|
||||
w2 = BlockingWormhole(appid, self.relayurl)
|
||||
w1 = BlockingWormhole(APPID, self.relayurl)
|
||||
w2 = BlockingWormhole(APPID, self.relayurl)
|
||||
d = deferToThread(w1.get_code)
|
||||
def _got_code(code):
|
||||
w2.set_code(code+"not")
|
||||
|
@ -123,15 +120,14 @@ class Blocking(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
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_data)
|
||||
self.assertRaises(UsageError, w1.send_data, b"data")
|
||||
w1.set_code("123-purple-elephant")
|
||||
self.assertRaises(UsageError, w1.set_code, "123-nope")
|
||||
self.assertRaises(UsageError, w1.get_code)
|
||||
w2 = BlockingWormhole(appid, self.relayurl)
|
||||
w2 = BlockingWormhole(APPID, self.relayurl)
|
||||
d = deferToThread(w2.get_code)
|
||||
def _done(code):
|
||||
self.assertRaises(UsageError, w2.get_code)
|
||||
|
@ -140,10 +136,9 @@ class Blocking(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_serialize(self):
|
||||
appid = b"appid"
|
||||
w1 = BlockingWormhole(appid, self.relayurl)
|
||||
w1 = BlockingWormhole(APPID, self.relayurl)
|
||||
self.assertRaises(UsageError, w1.serialize) # too early
|
||||
w2 = BlockingWormhole(appid, self.relayurl)
|
||||
w2 = BlockingWormhole(APPID, self.relayurl)
|
||||
d = deferToThread(w1.get_code)
|
||||
def _got_code(code):
|
||||
self.assertRaises(UsageError, w2.serialize) # too early
|
||||
|
|
|
@ -4,15 +4,16 @@ from twisted.internet.defer import gatherResults
|
|||
from ..twisted.transcribe import Wormhole, UsageError
|
||||
from .common import ServerBase
|
||||
|
||||
APPID = u"appid"
|
||||
|
||||
class Basic(ServerBase, unittest.TestCase):
|
||||
|
||||
def doBoth(self, d1, d2):
|
||||
return gatherResults([d1, d2], True)
|
||||
|
||||
def test_basic(self):
|
||||
appid = b"appid"
|
||||
w1 = Wormhole(appid, self.relayurl)
|
||||
w2 = Wormhole(appid, self.relayurl)
|
||||
w1 = Wormhole(APPID, self.relayurl)
|
||||
w2 = Wormhole(APPID, self.relayurl)
|
||||
d = w1.get_code()
|
||||
def _got_code(code):
|
||||
w2.set_code(code)
|
||||
|
@ -30,9 +31,8 @@ class Basic(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_interleaved(self):
|
||||
appid = b"appid"
|
||||
w1 = Wormhole(appid, self.relayurl)
|
||||
w2 = Wormhole(appid, self.relayurl)
|
||||
w1 = Wormhole(APPID, self.relayurl)
|
||||
w2 = Wormhole(APPID, self.relayurl)
|
||||
d = w1.get_code()
|
||||
def _got_code(code):
|
||||
w2.set_code(code)
|
||||
|
@ -51,9 +51,8 @@ class Basic(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_fixed_code(self):
|
||||
appid = b"appid"
|
||||
w1 = Wormhole(appid, self.relayurl)
|
||||
w2 = Wormhole(appid, self.relayurl)
|
||||
w1 = Wormhole(APPID, self.relayurl)
|
||||
w2 = Wormhole(APPID, self.relayurl)
|
||||
w1.set_code("123-purple-elephant")
|
||||
w2.set_code("123-purple-elephant")
|
||||
d = self.doBoth(w1.send_data(b"data1"), w2.send_data(b"data2"))
|
||||
|
@ -69,9 +68,8 @@ class Basic(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_verifier(self):
|
||||
appid = b"appid"
|
||||
w1 = Wormhole(appid, self.relayurl)
|
||||
w2 = Wormhole(appid, self.relayurl)
|
||||
w1 = Wormhole(APPID, self.relayurl)
|
||||
w2 = Wormhole(APPID, self.relayurl)
|
||||
d = w1.get_code()
|
||||
def _got_code(code):
|
||||
w2.set_code(code)
|
||||
|
@ -95,9 +93,8 @@ class Basic(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_verifier_mismatch(self):
|
||||
appid = b"appid"
|
||||
w1 = Wormhole(appid, self.relayurl)
|
||||
w2 = Wormhole(appid, self.relayurl)
|
||||
w1 = Wormhole(APPID, self.relayurl)
|
||||
w2 = Wormhole(APPID, self.relayurl)
|
||||
d = w1.get_code()
|
||||
def _got_code(code):
|
||||
w2.set_code(code+"not")
|
||||
|
@ -112,15 +109,14 @@ class Basic(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
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.send_data, b"data")
|
||||
self.assertRaises(UsageError, w1.get_data)
|
||||
w1.set_code("123-purple-elephant")
|
||||
self.assertRaises(UsageError, w1.set_code, "123-nope")
|
||||
self.assertRaises(UsageError, w1.get_code)
|
||||
w2 = Wormhole(appid, self.relayurl)
|
||||
w2 = Wormhole(APPID, self.relayurl)
|
||||
d = w2.get_code()
|
||||
self.assertRaises(UsageError, w2.get_code)
|
||||
def _got_code(code):
|
||||
|
@ -129,10 +125,9 @@ class Basic(ServerBase, unittest.TestCase):
|
|||
return d
|
||||
|
||||
def test_serialize(self):
|
||||
appid = b"appid"
|
||||
w1 = Wormhole(appid, self.relayurl)
|
||||
w1 = Wormhole(APPID, self.relayurl)
|
||||
self.assertRaises(UsageError, w1.serialize) # too early
|
||||
w2 = Wormhole(appid, self.relayurl)
|
||||
w2 = Wormhole(APPID, self.relayurl)
|
||||
d = w1.get_code()
|
||||
def _got_code(code):
|
||||
self.assertRaises(UsageError, w2.serialize) # too early
|
||||
|
|
|
@ -5,7 +5,7 @@ from twisted.internet import reactor
|
|||
from .transcribe import Wormhole
|
||||
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)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from __future__ import print_function
|
||||
import os, sys, json, re
|
||||
import os, sys, json, re, unicodedata
|
||||
from binascii import hexlify, unhexlify
|
||||
from zope.interface import implementer
|
||||
from twisted.internet import reactor, defer
|
||||
|
@ -17,6 +17,9 @@ from ..errors import ServerError, WrongPasswordError, UsageError
|
|||
from ..util.hkdf import HKDF
|
||||
from ..channel_monitor import monitor
|
||||
|
||||
def to_bytes(u):
|
||||
return unicodedata.normalize("NFC", u).encode("utf-8")
|
||||
|
||||
@implementer(IBodyProducer)
|
||||
class DataProducer:
|
||||
def __init__(self, data):
|
||||
|
@ -146,7 +149,7 @@ class Wormhole:
|
|||
version_warning_displayed = False
|
||||
|
||||
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 relay_url.endswith(u"/"): raise UsageError
|
||||
self._appid = appid
|
||||
|
@ -219,7 +222,7 @@ class Wormhole:
|
|||
def _start(self):
|
||||
# allocate the rest now too, so it can be serialized
|
||||
self.sp = SPAKE2_Symmetric(self.code.encode("ascii"),
|
||||
idSymmetric=self._appid)
|
||||
idSymmetric=to_bytes(self._appid))
|
||||
self.msg1 = self.sp.start()
|
||||
|
||||
def serialize(self):
|
||||
|
@ -242,7 +245,7 @@ class Wormhole:
|
|||
@classmethod
|
||||
def from_serialized(klass, 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_code_and_channelid(d["code"].encode("ascii"))
|
||||
self.sp = SPAKE2_Symmetric.from_serialized(json.dumps(d["spake2"]))
|
||||
|
@ -250,11 +253,11 @@ class Wormhole:
|
|||
return self
|
||||
|
||||
def derive_key(self, purpose, length=SecretBox.KEY_SIZE):
|
||||
if not isinstance(purpose, type(u"")): raise UsageError
|
||||
if self.key is None:
|
||||
# call after get_verifier() or get_data()
|
||||
raise UsageError
|
||||
if not isinstance(purpose, type(b"")): raise UsageError
|
||||
return HKDF(self.key, length, CTXinfo=purpose)
|
||||
return HKDF(self.key, length, CTXinfo=to_bytes(purpose))
|
||||
|
||||
def _encrypt_data(self, key, data):
|
||||
assert isinstance(key, type(b"")), type(key)
|
||||
|
@ -282,7 +285,7 @@ class Wormhole:
|
|||
def _got_pake(pake_msg):
|
||||
key = self.sp.finish(pake_msg)
|
||||
self.key = key
|
||||
self.verifier = self.derive_key(self._appid+b":Verifier")
|
||||
self.verifier = self.derive_key(self._appid+u":Verifier")
|
||||
return key
|
||||
d.addCallback(_got_pake)
|
||||
return d
|
||||
|
@ -304,7 +307,7 @@ class Wormhole:
|
|||
# ignores reflections.
|
||||
d = self._get_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)
|
||||
return self.channel.send(u"data", outbound_encrypted)
|
||||
d.addCallback(_send)
|
||||
|
@ -316,7 +319,7 @@ class Wormhole:
|
|||
if self.channel is None: raise UsageError
|
||||
d = self._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")
|
||||
def _decrypt(inbound_encrypted):
|
||||
try:
|
||||
|
|
Loading…
Reference in New Issue
Block a user