尝试用python做音频的LSB隐藏算法,过程是曲折的,虽然结果还有一些瑕疵,毕竟对音频的信号处理还不太熟练,不过结果总算是成功了。
1.环境
win10操作系统、pycharm编辑器、python3.9、第三方库:librosa、bitarray
1.1安装bitarray
之所以选择安装bitarray是为了解决中英文字符串和二进制字符串的转化问题
pip install bitarray
1.2安装librosa
它是一个Python模块,通常用于分析音频信号,但更倾向于音乐。它包括用于构建MIR(音乐信息检索)系统的nuts 和 bolts。
pip install librosa
2.转码函数
def str2bitarray(s):
s.replace(r'\n', '')
ret = bitarray(''.join([bin(int('1' + hex(c)[2:], 16))[3:] for c in s.encode('utf-8')]))
return ret
def bitarray2str(bit):
bit = bitarray(bit)
print(bit)
return bit.tobytes().decode('utf-8')
str2bitarray()函数:作用是将文本文件中读取出字符串转码为二进制字符串
bitarray2str()函数:作用是将解密后的二进制字符串转码为明文
3. 原理
上述是图像的lsb隐藏算法,音频也是类似的
一言以蔽之,假如某个音频的采样点为32位float类型,那么替换掉其中一些不太重要的位置,对音频的影响不大,极难发现。如0.005689改为0.005680。
4.算法实现
import librosa
from bitarray import bitarray
import soundfile as sf
import numpy as np
bit_float = {'00': 0.000001, '01': 0.000002, '10': 0.000003, '11': 0.000004}
float_bit = {8:'00',9:'00',10:'00',11:'00',18:'01',19:'01',20:'01',21:'01',28:'10',29:'10',30:'10',31:'10',38:'11',39:'11',40:'11',41:'11'}
def str2bitarray(s):
s.replace(r'\n', '')
ret = bitarray(''.join([bin(int('1' + hex(c)[2:], 16))[3:] for c in s.encode('utf-8')]))
return ret
def bitarray2str(bit):
bit = bitarray(bit)
print(bit)
return bit.tobytes().decode('utf-8')
def bit2float(bit):
if bit.to01() in bit_float.keys():
return bit_float[bit.to01()]
def float2bit(float):
if float in bit_float.values():
for key, value in bit_float.items():
if float == value:
return key
def LSB(audiofile, hidefile):
data, sr = librosa.load(audiofile)
#隐藏的文本
infor = ''
with open(hidefile, 'rb') as f:
infor = str2bitarray(f.read().decode('utf-8'))
start = 0
end = 2
for i in range(len(infor)):
if bit2float(infor[start:end]) == None:
break
data[i] = data[i] + bit2float(infor[start:end])
start += 2
end += 2
#librosa.output.write_wav(r'C:\Users\adins\Desktop\example.wav', data)
sf.write(r'C:\Users\adins\Desktop\example.wav', data,samplerate=sr, subtype='PCM_24')
def UNLSB(audiofile , hidedfile):
data, sr = librosa.load(audiofile)
hidedata , sr1 = librosa.load(hidedfile)
infor = ''
for i in range(len(data)):
item = int((hidedata[i] - data[i])*10000000)
if item in float_bit.keys():
infor = infor + float_bit[item]
if item < 5:
break
infor = bitarray2str(infor)
with open(r'..\2.txt', 'w') as f:
f.write(infor)
LSB(r'C:\Users\adins\Desktop\0020a.wav', r'C:\Users\adins\Desktop\1.txt')
UNLSB(r'C:\Users\adins\Desktop\0020a.wav', r'C:\Users\adins\Desktop\example.wav')
LSB('音频载体_WAV格式',’密文‘):加密函数
UNLSB('加密前音频',’加密后的音频‘):解密函数
4.1 缺陷
由于python没有指针,故没有好办法来对float型采样点做某一位上的操作,这里采用的是投机取巧的办法,定义了一个字典bit_float = {'00': 0.000001, '01': 0.000002, '10': 0.000003, '11': 0.000004}
使得将每两位二进制转化为float型与原采样点相加,再用原采样点与加密后的采样点的差值作为提取时的判断标准。这样带来的缺点十分明显,第一解密时需要与原载体做对比才能导出明文,第二,由于是做的float型加法,所得为近似值,不够稳定,第三这种不是对某些位的操作会造成信息的利用率不高,嵌入容量低。
为了解决float型加减时为近似计算的弊端,在用隐藏信息的采样点减去原采样点时*10000000
int((hidedata[i] - data[i])*10000000)转为整数,取8、9、10、11近似为10,18、19、20、21近似为20,28、29、30、31近似为30。创建一个字典用来做转换
float_bit = {8:'00',9:'00',10:'00',11:'00',18:'01',19:'01',20:'01',21:'01',28:'10',29:'10',30:'10',31:'10',38:'11',39:'11',40:'11',41:'11'}
即差值*10000000为10的对应二进制’00‘,这也算做了一种编码`~`。
4.2加密对比
加密前后的波形图无明显区别
测试中英文均可行。