Skip to content

Commit

Permalink
Add support for bit manipulation of non power of two byte depths
Browse files Browse the repository at this point in the history
  • Loading branch information
ragibson committed Jan 26, 2024
1 parent 07314f1 commit bc09802
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 9 deletions.
9 changes: 2 additions & 7 deletions stego_lsb/bit_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
import numpy as np
import os

byte_depth_to_dtype = {1: np.uint8, 2: np.uint16, 4: np.uint32, 8: np.uint64}


def roundup(x: float, base: int = 1) -> int:
return int(ceil(x / base)) * base
Expand All @@ -53,10 +51,8 @@ def lsb_interleave_bytes(carrier: bytes, payload: bytes, num_lsb: int, truncate:
bit_height = roundup(plen * 8 / num_lsb)
payload_bits.resize(bit_height * num_lsb)

carrier_dtype = byte_depth_to_dtype[byte_depth]
carrier_bits = np.unpackbits(np.frombuffer(carrier, dtype=carrier_dtype, count=bit_height).view(np.uint8)
carrier_bits = np.unpackbits(np.frombuffer(carrier, dtype=np.uint8, count=byte_depth * bit_height)
).reshape(bit_height, 8 * byte_depth)

carrier_bits[:, 8 * byte_depth - num_lsb: 8 * byte_depth] = payload_bits.reshape(bit_height, num_lsb)

ret = np.packbits(carrier_bits).tobytes()
Expand All @@ -75,8 +71,7 @@ def lsb_deinterleave_bytes(carrier: bytes, num_bits: int, num_lsb: int, byte_dep
"""

plen = roundup(num_bits / num_lsb)
carrier_dtype = byte_depth_to_dtype[byte_depth]
payload_bits = np.unpackbits(np.frombuffer(carrier, dtype=carrier_dtype, count=plen).view(np.uint8)
payload_bits = np.unpackbits(np.frombuffer(carrier, dtype=np.uint8, count=byte_depth * plen)
).reshape(plen, 8 * byte_depth)[:, 8 * byte_depth - num_lsb: 8 * byte_depth]
return np.packbits(payload_bits).tobytes()[: num_bits // 8]

Expand Down
21 changes: 19 additions & 2 deletions tests/test_bit_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ def check_random_interleaving(self, byte_depth: int = 1, num_trials: int = 1024)
np.random.seed(0)
for _ in range(num_trials):
carrier_len = np.random.randint(1, 16384)

# round up carrier length to next multiple of byte depth
carrier_len += (byte_depth - carrier_len) % byte_depth
assert carrier_len % byte_depth == 0

num_lsb = np.random.randint(1, 8 * byte_depth + 1)
payload_len = carrier_len * num_lsb // (8 * byte_depth)
carrier = np.random.randint(0, 256, size=carrier_len).tobytes()
payload = np.random.randint(0, 256, size=payload_len).tobytes()
carrier = np.random.randint(0, 256, size=carrier_len, dtype=np.uint8).tobytes()
payload = np.random.randint(0, 256, size=payload_len, dtype=np.uint8).tobytes()
self.assertConsistentInterleaving(carrier, payload, num_lsb, byte_depth=byte_depth)

def test_interleaving_consistency_8bit(self) -> None:
Expand All @@ -32,9 +37,21 @@ def test_interleaving_consistency_8bit(self) -> None:
def test_interleaving_consistency_16bit(self) -> None:
self.check_random_interleaving(byte_depth=2)

def test_interleaving_consistency_24bit(self) -> None:
self.check_random_interleaving(byte_depth=3)

def test_interleaving_consistency_32bit(self) -> None:
self.check_random_interleaving(byte_depth=4)

def test_interleaving_consistency_40bit(self) -> None:
self.check_random_interleaving(byte_depth=5)

def test_interleaving_consistency_48bit(self) -> None:
self.check_random_interleaving(byte_depth=6)

def test_interleaving_consistency_56bit(self) -> None:
self.check_random_interleaving(byte_depth=7)

def test_interleaving_consistency_64bit(self) -> None:
self.check_random_interleaving(byte_depth=8)

Expand Down

0 comments on commit bc09802

Please sign in to comment.