102 lines
2.7 KiB
Python
102 lines
2.7 KiB
Python
"""Tests for the SecureBuffer mlock'd, no-core-dump, wipe-on-close buffer.
|
|
|
|
CPython-only: we rely on reference counting to free memoryviews as soon
|
|
as they go out of scope (a `del` or function return), so no release()
|
|
calls or view context managers are needed. Live exported views would
|
|
make mmap.close() fail with BufferError, so view names are del'd before
|
|
the owning SecureBuffer is closed.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from quantumpipe.chab3 import decrypt, decrypt_into, encrypt, encrypt_into
|
|
from quantumpipe.securebuf import SecureBuffer, wipe
|
|
|
|
|
|
def test_buffer_protocol_read_write():
|
|
with SecureBuffer(32) as sb:
|
|
assert len(sb) == 32
|
|
mv = memoryview(sb)
|
|
assert len(mv) == 32
|
|
assert not mv.readonly
|
|
mv[:4] = b"abcd"
|
|
assert mv[:4] == b"abcd"
|
|
del mv
|
|
assert sb.closed
|
|
|
|
|
|
def test_not_convertible_to_bytes():
|
|
with SecureBuffer(16) as sb, pytest.raises(TypeError, match="cannot be converted"):
|
|
bytes(sb)
|
|
|
|
|
|
def test_wipe_utility_fills_ff():
|
|
sb = SecureBuffer(16)
|
|
sb[:] = b"A" * 16
|
|
wipe(sb)
|
|
mv = memoryview(sb)
|
|
assert mv.tobytes() == b"\xff" * 16
|
|
del mv
|
|
sb.close()
|
|
|
|
|
|
def test_close_is_idempotent():
|
|
sb = SecureBuffer(16)
|
|
sb.close()
|
|
assert sb.closed
|
|
sb.close() # second close is a no-op
|
|
assert sb.closed
|
|
|
|
|
|
def test_context_manager_closes():
|
|
with SecureBuffer(16) as sb:
|
|
pass
|
|
assert sb.closed
|
|
|
|
|
|
def test_context_manager_closes_on_exception():
|
|
with pytest.raises(RuntimeError), SecureBuffer(16) as sb:
|
|
raise RuntimeError("boom")
|
|
assert sb.closed
|
|
|
|
|
|
def test_del_closes():
|
|
import gc
|
|
|
|
sb = SecureBuffer(16)
|
|
ref = sb._mmap
|
|
del sb
|
|
gc.collect()
|
|
assert ref.closed
|
|
|
|
|
|
def test_roundtrip_through_chab3():
|
|
"""SecureBuffer works as key input and as an `into` target."""
|
|
with SecureBuffer(32) as key, SecureBuffer(64) as box, SecureBuffer(16) as pt:
|
|
key[:] = bytes(range(32))
|
|
box_view = encrypt_into(box, b"hello", key)
|
|
pt_view = decrypt_into(pt, box_view, key)
|
|
assert pt_view == b"hello"
|
|
del box_view, pt_view
|
|
|
|
|
|
def test_key_material_needs_no_bytes_copy():
|
|
"""chab3 accepts the SecureBuffer directly -- no bytes() conversion."""
|
|
with SecureBuffer(32) as key:
|
|
key[:] = b"\x07" * 32
|
|
ct = encrypt(b"secret", key)
|
|
assert decrypt(ct, key) == b"secret"
|
|
|
|
|
|
def test_getitem_returns_memoryview():
|
|
with SecureBuffer(16) as sb:
|
|
sb[:4][:] = b"abcd"
|
|
view = sb[2:8]
|
|
assert isinstance(view, memoryview)
|
|
assert view.tobytes() == b"cd\x00\x00\x00\x00"
|
|
view[:2] = b"XY"
|
|
xy = sb[2:4]
|
|
assert xy.tobytes() == b"XY"
|
|
assert sb[0] == ord("a")
|
|
del view, xy
|