Skip to content

Instantly share code, notes, and snippets.

@tam17aki
Last active May 12, 2020 09:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tam17aki/ae347903dcaadea29f4ef7191e1369ac to your computer and use it in GitHub Desktop.
Save tam17aki/ae347903dcaadea29f4ef7191e1369ac to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
""" Wavデータに対して、LSBにメッセージを隠す. """
# Copyright (C) 2020 Akira TAMAMORI
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import stegano.tools as tools
import soundfile as sf
import numpy as np
from scipy.io.wavfile import write as wav_write
from scipy.io.wavfile import read as wav_read
def hide(infile: str, # 元のwav
outfile: str, # メッセージを隠したwav
message: str, # 隠すメッセージ
encoding: str = "UTF-8"):
""" 最下位ビット(LSB)にメッセージを隠す """
_, wav_data = wav_read(infile)
with sf.SoundFile(infile) as wav_file:
num_channel = wav_file.channels
num_frame = wav_file.frames
samplerate = wav_file.samplerate
# scipyのwav_readでは、dataのshapeが(num_frame, num_channel)
# →vectorへと形状変換する
if num_channel > 1:
wav_data = np.reshape(wav_data, [num_channel * num_frame, -1])
wav_data = wav_data.copy() # in order to overwrite
wav_data = wav_data.astype('int16') # short int型にキャスト
# 例えば 「12:Hello, World!」のように、メッセージ長と区切り文字を作る
# →メッセージの「ヘッダ」を付与するイメージ
message = str(len(message)) + ":" + str(message)
# メッセージを0と1からなる「文字列」に加工
# →各文字のasciiコードを2進表現した「文字列」を1つの文字列に連結
message_bits = "".join(tools.a2bits_list(message, encoding))
len_message_bits = len(message_bits)
index = 0
for _ in range(num_frame):
for _ in range(num_channel):
if index + 1 <= len_message_bits:
# 最下位ビットにメッセージを隠す
wav_data[index] = tools.setlsb(wav_data[index],
message_bits[index])
index = index + 1
# vectorを元のshapeに戻す
if num_channel > 1:
wav_data = np.reshape(wav_data, [num_frame, num_channel])
wav_write(outfile, samplerate, wav_data)
def reveal(infile: str, # LSBにメッセージが隠されたwav
encoding: str = "UTF-8"):
""" LSBに隠したメッセージを取り出す """
_, wav_data = wav_read(infile)
with sf.SoundFile(infile) as wav_file:
num_channel = wav_file.channels
num_frame = wav_file.frames
if num_channel > 1:
wav_data = np.reshape(wav_data, [num_channel * num_frame, -1])
ascii_code, count = 0, 0
message_chars = []
message_length = None
index = 0
hidden_message = None
for _ in range(num_frame):
for _ in range(num_channel):
# 最下位ビットを取得して
# →次に来るビットのため、あらかじめビットシフトして「空けておく」
ascii_code += (wav_data[index] & 1) \
<< (tools.ENCODINGS[encoding] - 1 - count)
count += 1
index += 1
# 8回ビットシフトしたらasciiに戻す(1文字は8bitなので)
# ←tools.ENCODINGS[encoding]の戻り値は8
if count == tools.ENCODINGS[encoding]:
# chr() は asciiコードをascii文字に戻す関数
message_chars.append(chr(ascii_code))
ascii_code, count = 0, 0 # カウントをリセット
# 末尾が区切り文字 かつ メッセージ未取得
if message_chars[-1] == ":" and message_length is None:
try:
# 区切り文字前の数字からメッセージの長さを確定
message_length = int("".join(message_chars[:-1]))
except Exception:
pass
# 区切り文字 ':' に続く文字列を隠しメッセージとして取得
if (len(message_chars) -
len(str(message_length)) - 1) == message_length:
# 区切り文字以降の文字を連結
hidden_message = "".join(message_chars)[
len(str(message_length)) + 1:]
return hidden_message
def main():
"""main routine"""
host_wav = './input/host.wav' # 元の音
stego_wav = './output/stegano.wav' # メッセージを隠した音
# LSB にメッセージを隠す
hide(infile=host_wav, outfile=stego_wav,
message="Hello, World!")
# 隠したメッセージを取り出す
message = reveal(stego_wav)
print(f"Hidden message is \"{message}\"")
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment