57 lines
2.2 KiB
Python
57 lines
2.2 KiB
Python
"""Test 1: does cryptography's into-API (CipherContext.update_into) accept an
|
|
immutable bytes object as the output buffer, despite bytes being read-only?
|
|
|
|
Python's buffer protocol will hand out a buffer descriptor for bytes (pyo3
|
|
requests PyBUF_FULL_RO), so the only thing stopping the write is an explicit
|
|
readonly check in cryptography's Rust binding. This script finds out, and
|
|
verifies correctness against the normal bytes-returning update() call.
|
|
"""
|
|
|
|
from cryptography.hazmat.primitives.ciphers import Cipher
|
|
from cryptography.hazmat.primitives.ciphers.algorithms import ChaCha20
|
|
|
|
KEY = bytes(range(32))
|
|
NONCE = bytes(16)
|
|
DATA = b"The quick brown fox jumps over the lazy dog"
|
|
|
|
|
|
def fresh_encryptor():
|
|
return Cipher(ChaCha20(KEY, NONCE), mode=None).encryptor()
|
|
|
|
|
|
def main():
|
|
expected = fresh_encryptor().update(DATA)
|
|
|
|
# control: writable bytearray must work
|
|
buf = bytearray(len(DATA))
|
|
n = fresh_encryptor().update_into(DATA, buf)
|
|
assert bytes(buf) == expected and n == len(DATA)
|
|
print(f"control bytearray : ok ({n} bytes written)")
|
|
|
|
# the hack: immutable bytes as the output buffer.
|
|
# Fresh object: refcount 1, zero-filled, cached hash not yet computed,
|
|
# so no other code can observe the pre-mutation state.
|
|
out = bytes(len(DATA))
|
|
print(f"before bytes({len(DATA)}) : {out[:16].hex()}...")
|
|
try:
|
|
n = fresh_encryptor().update_into(DATA, out)
|
|
except Exception as e: # noqa: BLE001 -- probing behavior, any error is the answer
|
|
print(f"bytes-hack : REJECTED {type(e).__name__}: {e}")
|
|
return
|
|
print(f"bytes-hack : ACCEPTED ({n} bytes written)")
|
|
print(f"after bytes({len(DATA)}) : {out[:16].hex()}...")
|
|
print(f"mutated in place : {out != bytes(len(DATA))}")
|
|
print(f"matches update() result : {out == expected}")
|
|
|
|
# and via an explicit memoryview (readonly flag should propagate)
|
|
try:
|
|
fresh_encryptor().update_into(DATA, memoryview(bytes(len(DATA))))
|
|
except Exception as e: # noqa: BLE001 -- probing behavior, any error is the answer
|
|
print(f"memoryview(bytes) : REJECTED {type(e).__name__}: {e}")
|
|
else:
|
|
print("memoryview(bytes) : ACCEPTED")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|