Audio Steganography¶
Hide secret messages or binary data within audio signals so that casual listeners hear only the original sound, while decoders can extract the hidden payload.
Two methods¶
Method |
Capacity |
Audibility |
Robustness |
Requirement |
|---|---|---|---|---|
LSB |
~1 bit/sample (~16 KB / 3 s @ 44.1 kHz) |
Inaudible (≈ −90 dB) |
Fragile (destroyed by lossy compression, resampling) |
Any sample rate |
Frequency-band |
~2.6 kbit/s (~121 bytes / 3 s @ 44.1 kHz) |
Above most listeners’ hearing |
Moderate (survives mild noise) |
sample_rate ≥ 40 kHz |
LSB flips the least-significant bit of a 16-bit PCM representation — distortion ≈ −90 dB. Best for lossless pipelines (WAV, FLAC).
Frequency-band encodes data as brief BFSK tone bursts at 18.5 kHz (bit 0) or 19.5 kHz (bit 1). Choose this when light interference is expected.
Message structure¶
Both methods prepend a 32-bit little-endian header: bits 0–30 hold the byte count, bit 31 indicates payload type (0 = text, 1 = binary). This lets the decoder recover messages without prior knowledge of length.
Hiding text¶
import pyminidsp as md
host = md.sine_wave(44100, amplitude=0.8, freq=440.0, sample_rate=44100.0)
stego, n = md.steg_encode(host, "secret message",
sample_rate=44100.0, method=md.STEG_LSB)
print(f"Encoded {n} bytes")
Listen — compare the host signal and the stego outputs:
Original host (440 Hz sine):
LSB-encoded (sounds identical):
Frequency-band encoded (faint high-frequency tones):
Recovering text¶
recovered = md.steg_decode(stego, sample_rate=44100.0, method=md.STEG_LSB)
print(recovered) # "secret message"
Binary data¶
data = b"\x00\x01\x02\xff\xfe\xfd"
stego, n = md.steg_encode_bytes(host, data, sample_rate=44100.0)
recovered = md.steg_decode_bytes(stego, sample_rate=44100.0)
assert recovered == data
Automatic detection¶
method, payload_type = md.steg_detect(stego, sample_rate=44100.0)
if method is not None:
print(f"Method: {'LSB' if method == md.STEG_LSB else 'Freq-band'}")
print(f"Type: {'text' if payload_type == md.STEG_TYPE_TEXT else 'binary'}")
Capacity check¶
cap = md.steg_capacity(44100, sample_rate=44100.0, method=md.STEG_LSB)
print(f"Can hide up to {cap} bytes")
md.shutdown()