Bases: BaseCodec
This module implements simple FFT steganography hide string in audio (wav).
Referenced from shalom06/Audio-Stego.
Source code in stegobox/codec/fft_hide_str_in_wav.py
| class FFTHideStrinWAV(BaseCodec):
"""
This module implements simple FFT steganography hide string in audio (wav).
Referenced from [shalom06/Audio-Stego](https://github.com/shalom06/Audio-Stego).
"""
def __init__(self) -> None:
super().__init__()
def encode(self, carrier: np.ndarray, payload: str) -> tuple[np.ndarray, int]:
"""
Encoder requires the carrier audio in format WAV and the payload in format
string.
Args:
carrier (np.ndarray): Carrier audio in format WAV. Read with
`stegobox.io.audio.wavfile_read()`.
payload (str): Payload (secret message) to be encoded.
Returns:
np.ndarray: the frame information of the audio with the payload embedded.
int: the rate of the audio with the payload embedded.
"""
# step 1 divide into chunks
carrier = carrier.copy()
payload_length = 8 * len(payload)
blocklength = int(2 * 2 ** np.ceil(np.log2(2 * payload_length)))
blocknumber = int(np.ceil(carrier.shape[0] / blocklength))
# checks shape to change data to 1 axis
if len(carrier.shape) == 1:
carrier.resize(blocknumber * blocklength, refcheck=False)
carrier = carrier[np.newaxis]
else:
carrier.resize(
(blocknumber * blocklength, carrier.shape[1]), refcheck=False
)
carrier = carrier.T
blocks = carrier[0].reshape((blocknumber, blocklength))
# Calculate DFT using fft
blocks = np.fft.fft(blocks)
# calculate magnitudes
magnitudes = np.abs(blocks)
# create phase matrix
phases = np.angle(blocks)
# get phase differences
phasediffs = np.diff(phases, axis=0)
# conert message to encode into binary
textinbinary = np.array(
[[int(y) for y in format(ord(x), "08b")] for x in payload]
)
textinbinary = textinbinary.reshape(-1)
# Convert txt to phase differences
textinpi = textinbinary.copy()
textinpi[textinpi == 0] = -1
textinpi = textinpi * -np.pi / 2
blockmid = blocklength // 2
# do phase conversion
phases[0, blockmid - payload_length : blockmid] = textinpi
phases[0, blockmid + 1 : blockmid + 1 + payload_length] = -textinpi[::-1]
# re compute the ophase amtrix
for i in range(1, len(phases)):
phases[i] = phases[i - 1] + phasediffs[i - 1]
# apply i-dft
blocks = magnitudes * np.exp(1j * phases)
blocks = np.fft.ifft(blocks).real
# combining all block of audio again
carrier[0] = blocks.ravel().astype(np.int16)
return carrier.T, payload_length
def decode(self, _):
raise NotImplementedError("This codec does not support decoding without length")
def decode_with_length(self, carrier: np.ndarray, payload_length: int) -> str:
"""
Decode the secret payload from the carrier audio
Args:
carrier (np.ndarray): Carrier audio in format WAV. Read with
`stegobox.io.audio.wavfile_read()`.
payload_length (int): The length of secret message.
Returns:
str: The decoded payload (secret message).
"""
blocklength = 2 * int(2 ** np.ceil(np.log2(2 * payload_length)))
blockmid = blocklength // 2
# get header info
if len(carrier.shape) == 1:
secret = carrier[:blocklength]
else:
secret = carrier[:blocklength, 0]
# get the phase and convert it to binary
secretphases = np.angle(np.fft.fft(secret))[
blockmid - payload_length : blockmid
]
secretinbinary = (secretphases < 0).astype(np.int8)
# convert into characters
secretincode = secretinbinary.reshape((-1, 8)).dot(
1 << np.arange(8 - 1, -1, -1)
)
# combine characters to original text
return "".join(np.char.mod("%c", secretincode))
|
encode(carrier, payload)
Encoder requires the carrier audio in format WAV and the payload in format
string.
Parameters:
Name |
Type |
Description |
Default |
carrier |
ndarray
|
Carrier audio in format WAV. Read with
stegobox.io.audio.wavfile_read() .
|
required
|
payload |
str
|
Payload (secret message) to be encoded.
|
required
|
Returns:
Name | Type |
Description |
|
ndarray
|
np.ndarray: the frame information of the audio with the payload embedded.
|
int |
int
|
the rate of the audio with the payload embedded.
|
Source code in stegobox/codec/fft_hide_str_in_wav.py
| def encode(self, carrier: np.ndarray, payload: str) -> tuple[np.ndarray, int]:
"""
Encoder requires the carrier audio in format WAV and the payload in format
string.
Args:
carrier (np.ndarray): Carrier audio in format WAV. Read with
`stegobox.io.audio.wavfile_read()`.
payload (str): Payload (secret message) to be encoded.
Returns:
np.ndarray: the frame information of the audio with the payload embedded.
int: the rate of the audio with the payload embedded.
"""
# step 1 divide into chunks
carrier = carrier.copy()
payload_length = 8 * len(payload)
blocklength = int(2 * 2 ** np.ceil(np.log2(2 * payload_length)))
blocknumber = int(np.ceil(carrier.shape[0] / blocklength))
# checks shape to change data to 1 axis
if len(carrier.shape) == 1:
carrier.resize(blocknumber * blocklength, refcheck=False)
carrier = carrier[np.newaxis]
else:
carrier.resize(
(blocknumber * blocklength, carrier.shape[1]), refcheck=False
)
carrier = carrier.T
blocks = carrier[0].reshape((blocknumber, blocklength))
# Calculate DFT using fft
blocks = np.fft.fft(blocks)
# calculate magnitudes
magnitudes = np.abs(blocks)
# create phase matrix
phases = np.angle(blocks)
# get phase differences
phasediffs = np.diff(phases, axis=0)
# conert message to encode into binary
textinbinary = np.array(
[[int(y) for y in format(ord(x), "08b")] for x in payload]
)
textinbinary = textinbinary.reshape(-1)
# Convert txt to phase differences
textinpi = textinbinary.copy()
textinpi[textinpi == 0] = -1
textinpi = textinpi * -np.pi / 2
blockmid = blocklength // 2
# do phase conversion
phases[0, blockmid - payload_length : blockmid] = textinpi
phases[0, blockmid + 1 : blockmid + 1 + payload_length] = -textinpi[::-1]
# re compute the ophase amtrix
for i in range(1, len(phases)):
phases[i] = phases[i - 1] + phasediffs[i - 1]
# apply i-dft
blocks = magnitudes * np.exp(1j * phases)
blocks = np.fft.ifft(blocks).real
# combining all block of audio again
carrier[0] = blocks.ravel().astype(np.int16)
return carrier.T, payload_length
|
decode_with_length(carrier, payload_length)
Decode the secret payload from the carrier audio
Parameters:
Name |
Type |
Description |
Default |
carrier |
ndarray
|
Carrier audio in format WAV. Read with
stegobox.io.audio.wavfile_read() .
|
required
|
payload_length |
int
|
The length of secret message.
|
required
|
Returns:
Name | Type |
Description |
str |
str
|
The decoded payload (secret message).
|
Source code in stegobox/codec/fft_hide_str_in_wav.py
| def decode_with_length(self, carrier: np.ndarray, payload_length: int) -> str:
"""
Decode the secret payload from the carrier audio
Args:
carrier (np.ndarray): Carrier audio in format WAV. Read with
`stegobox.io.audio.wavfile_read()`.
payload_length (int): The length of secret message.
Returns:
str: The decoded payload (secret message).
"""
blocklength = 2 * int(2 ** np.ceil(np.log2(2 * payload_length)))
blockmid = blocklength // 2
# get header info
if len(carrier.shape) == 1:
secret = carrier[:blocklength]
else:
secret = carrier[:blocklength, 0]
# get the phase and convert it to binary
secretphases = np.angle(np.fft.fft(secret))[
blockmid - payload_length : blockmid
]
secretinbinary = (secretphases < 0).astype(np.int8)
# convert into characters
secretincode = secretinbinary.reshape((-1, 8)).dot(
1 << np.arange(8 - 1, -1, -1)
)
# combine characters to original text
return "".join(np.char.mod("%c", secretincode))
|