PySecretHandshake/secret_handshake/boxstream.py

91 lines
2.5 KiB
Python
Raw Normal View History

2017-06-04 19:50:49 +00:00
import struct
from asyncio import IncompleteReadError
2017-08-01 20:06:29 +00:00
from async_generator import async_generator, yield_
2017-06-04 19:50:49 +00:00
from nacl.secret import SecretBox
2018-02-04 14:50:56 +00:00
from .util import inc_nonce, split_chunks
2017-06-04 19:50:49 +00:00
HEADER_LENGTH = 2 + 16 + 16
MAX_SEGMENT_SIZE = 4 * 1024
TERMINATION_HEADER = (b'\x00' * 18)
def get_stream_pair(reader, writer, **kwargs):
"""Return a tuple with `(unbox_stream, box_stream)` (reader/writer).
:return: (:class:`secret_handshake.boxstream.UnboxStream`,
:class:`secret_handshake.boxstream.BoxStream`) """
box_args = {
'key': kwargs['encrypt_key'],
'nonce': kwargs['encrypt_nonce'],
}
unbox_args = {
'key': kwargs['decrypt_key'],
'nonce': kwargs['decrypt_nonce'],
}
return UnboxStream(reader, **unbox_args), BoxStream(writer, **box_args)
class UnboxStream(object):
def __init__(self, reader, key, nonce):
self.reader = reader
self.key = key
self.nonce = nonce
self.closed = False
async def read(self):
try:
data = await self.reader.readexactly(HEADER_LENGTH)
except IncompleteReadError:
2017-06-04 19:50:49 +00:00
self.closed = True
return None
box = SecretBox(self.key)
header = box.decrypt(data, self.nonce)
if header == TERMINATION_HEADER:
self.closed = True
return None
length = struct.unpack('>H', header[:2])[0]
mac = header[2:]
data = await self.reader.readexactly(length)
2017-06-04 19:50:49 +00:00
body = box.decrypt(mac + data, inc_nonce(self.nonce))
self.nonce = inc_nonce(inc_nonce(self.nonce))
return body
2017-08-01 20:06:29 +00:00
@async_generator
2017-06-04 19:50:49 +00:00
async def __aiter__(self):
while True:
data = await self.read()
if data is None:
return
2017-08-01 20:06:29 +00:00
await yield_(data)
2017-06-04 19:50:49 +00:00
class BoxStream(object):
def __init__(self, writer, key, nonce):
self.writer = writer
self.key = key
self.box = SecretBox(self.key)
self.nonce = nonce
def write(self, data):
2017-06-05 09:35:55 +00:00
for chunk in split_chunks(data, MAX_SEGMENT_SIZE):
body = self.box.encrypt(chunk, inc_nonce(self.nonce))[24:]
header = struct.pack('>H', len(body) - 16) + body[:16]
2017-06-04 19:50:49 +00:00
2017-06-05 09:35:55 +00:00
hdrbox = self.box.encrypt(header, self.nonce)[24:]
self.writer.write(hdrbox)
2017-06-04 19:50:49 +00:00
2017-06-05 09:35:55 +00:00
self.nonce = inc_nonce(inc_nonce(self.nonce))
self.writer.write(body[16:])
2017-06-04 19:50:49 +00:00
def close(self):
self.writer.write(self.box.encrypt(b'\x00' * 18, self.nonce)[24:])