Skip to content

LSBWav

stegobox.codec.LSBWav

Bases: BaseCodec

This module implements simple LSB steganography in WAV audios.

with code shamelessly taken from https://daniellerch.me/stego/lab/intro/lsb-en/#lsb-steganography-in-wav-audio-files

Source code in stegobox/codec/lsb_wav.py
class LSBWav(BaseCodec):
    """
    This module implements simple LSB steganography in WAV audios.

    with code shamelessly taken from
    https://daniellerch.me/stego/lab/intro/lsb-en/#lsb-steganography-in-wav-audio-files
    """

    def __init__(self) -> None:
        super().__init__()

    def encode(
        self, carrier: wave.Wave_read, payload: str
    ) -> tuple[bytes, wave._wave_params]:
        """Encoder requires the carrier audio to be WAV and the payload to be a string.

        Args:
            carrier: Carrier audio in format WAV. Read with `stegobox.io.audio.read()`.
            payload: Payload (secret message) to be encoded.

        Returns:
            bytes: Frame information in bytes of the audio with the payload embedded.
            wave._wave_params: Parameters for audio with the payload embedded.
        """
        self._check_empty_payload(payload)

        # Read audio frame information
        frames = bytearray(carrier.readframes(carrier.getnframes()))

        # Convert payload into bits
        message_bits = []
        for pl in payload:
            message_bits += [(ord(pl) >> i) & 1 for i in range(8)]

        # LSB matching
        for i in range(len(message_bits)):
            if frames[i] % 2 != message_bits[i]:
                if frames[i] == 255:
                    s = -1
                elif frames[i] == 0:
                    s = +1
                else:
                    s = random.choice([-1, +1])
                frames[i] = frames[i] + s

        return bytes(frames), carrier.getparams()

    def decode(self, carrier: wave.Wave_read, payload_length: int = 20) -> str:
        """Decode the secret payload from the carrier audio

        Args:
            carrier: Carrier audio in format WAV. Read with `stegobox.io.audio.read()`.
            payload_length: the length of secret message

        Returns:
            The decoded payload (secret message).
        """
        # Read audio frame information
        frames = bytearray(carrier.readframes(carrier.getnframes()))

        # Get the least significant bits of each frame of audio
        message_bits = [int(f) % 2 for f in frames]

        # Convert bits into bytes
        message_ex = []
        value = 0
        for i in range(len(message_bits)):
            if i % 8 == 0 and i != 0:
                message_ex.append(value)
                value = 0
            value |= message_bits[i] << i % 8

        # Generates information based on bytes
        payload = "".join([chr(L) for L in message_ex])[:payload_length]
        return payload

    def _check_empty_payload(self, payload: str) -> None:
        if not payload:
            raise Exception("Payload must not be empty.")

encode(carrier, payload)

Encoder requires the carrier audio to be WAV and the payload to be a string.

Parameters:

Name Type Description Default
carrier Wave_read

Carrier audio in format WAV. Read with stegobox.io.audio.read().

required
payload str

Payload (secret message) to be encoded.

required

Returns:

Name Type Description
bytes bytes

Frame information in bytes of the audio with the payload embedded.

_wave_params

wave._wave_params: Parameters for audio with the payload embedded.

Source code in stegobox/codec/lsb_wav.py
def encode(
    self, carrier: wave.Wave_read, payload: str
) -> tuple[bytes, wave._wave_params]:
    """Encoder requires the carrier audio to be WAV and the payload to be a string.

    Args:
        carrier: Carrier audio in format WAV. Read with `stegobox.io.audio.read()`.
        payload: Payload (secret message) to be encoded.

    Returns:
        bytes: Frame information in bytes of the audio with the payload embedded.
        wave._wave_params: Parameters for audio with the payload embedded.
    """
    self._check_empty_payload(payload)

    # Read audio frame information
    frames = bytearray(carrier.readframes(carrier.getnframes()))

    # Convert payload into bits
    message_bits = []
    for pl in payload:
        message_bits += [(ord(pl) >> i) & 1 for i in range(8)]

    # LSB matching
    for i in range(len(message_bits)):
        if frames[i] % 2 != message_bits[i]:
            if frames[i] == 255:
                s = -1
            elif frames[i] == 0:
                s = +1
            else:
                s = random.choice([-1, +1])
            frames[i] = frames[i] + s

    return bytes(frames), carrier.getparams()

decode(carrier, payload_length=20)

Decode the secret payload from the carrier audio

Parameters:

Name Type Description Default
carrier Wave_read

Carrier audio in format WAV. Read with stegobox.io.audio.read().

required
payload_length int

the length of secret message

20

Returns:

Type Description
str

The decoded payload (secret message).

Source code in stegobox/codec/lsb_wav.py
def decode(self, carrier: wave.Wave_read, payload_length: int = 20) -> str:
    """Decode the secret payload from the carrier audio

    Args:
        carrier: Carrier audio in format WAV. Read with `stegobox.io.audio.read()`.
        payload_length: the length of secret message

    Returns:
        The decoded payload (secret message).
    """
    # Read audio frame information
    frames = bytearray(carrier.readframes(carrier.getnframes()))

    # Get the least significant bits of each frame of audio
    message_bits = [int(f) % 2 for f in frames]

    # Convert bits into bytes
    message_ex = []
    value = 0
    for i in range(len(message_bits)):
        if i % 8 == 0 and i != 0:
            message_ex.append(value)
            value = 0
        value |= message_bits[i] << i % 8

    # Generates information based on bytes
    payload = "".join([chr(L) for L in message_ex])[:payload_length]
    return payload