Files
quantumtunnel/tests/test_securebuf.py
T
2026-10-07 03:07:34 +00:00

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