Search code examples
pythonlsb

Audio Steganography using lsb causing noise in audio with hidden message


I am trying to solve a problem in my audio steganography code. Afted hiding the message in wav audio file, there is some noice which of course should not be there considering the point of the whole audio steganography. Thanks a lot for help !

here is the code


import wave
import os

global chosen_audio


def hide():
    hide_menu()
    global chosen_audio
    message = input('Input message to be hidden: \n')
    print('hiding message ... \n')
    audio = wave.open(chosen_audio, 'rb')
    frame_bytes = bytearray(list(audio.readframes(audio.getnframes())))
    message = message + int((len(frame_bytes) - (len(message) * 8 * 8)) / 8) * '#'
    bits = list(
        map(int, ''.join([bin(ord(i)).lstrip('0b').rjust(8, '0') for i in message])))

    for i, bit in enumerate(bits):
        frame_bytes[i] = (frame_bytes[i] & 254) | bit  # replace lsb
    frames_modified = bytes(frame_bytes)
    with wave.open('C:/Users/*****/PycharmProjects/steganography/modified_audio.wav', 'wb') as modified_audio:
        modified_audio.setparams(audio.getparams())
        modified_audio.writeframes(frames_modified)
    print('message hidden ... \n')
    modified_audio.close()
    audio.close()


def reveal():
    modified_audio = wave.open('C:/Users/*****/PycharmProjects/steganography/modified_audio.wav', 'rb')

    frame_bytes = bytearray(list(modified_audio.readframes(modified_audio.getnframes())))

    ls_bits = [frame_bytes[i] & 1 for i in range(len(frame_bytes))]

    text = "".join(chr(int("".join(map(str, ls_bits[i:i + 8])), 2)) for i in range(0, len(ls_bits), 8))
    message = text.split("###")[0]
    modified_audio.close()
    return message


def mode():
    method = input(
        ' \n\t\t\tPLEASE, CHOOSE THE PROCEDURE! \n\n \tPRESS H FOR HIDING THE MESSAGE  \t PRESS R FOR REVEALING THE MESSAGE FROM THE AUDIO\n')
    if method == 'H' or method == 'h':
        hide()
    elif method == 'r' or method == 'R':
        reveal()
    else:
        print('I don\'t think we have such a option')
        mode()


def hide_menu():
    global chosen_audio
    chosen_option = ''

    print(chosen_option)
    chosen_option = ''
    chosen_audio = ''
    print(' \nCHOOSE YOUR AUDIO FILE! ')
    chosen_option = (
        input('\t press 1 & ENTER for your own audio path\n''\t press 2 & ENTER for default audio file\n'))

    if chosen_option == '1':
        file_path = input('Enter a file path: ')

        if os.path.exists(file_path):
            print('The path is okay, file exists!')
            chosen_audio = file_path

        else:
            print('The specified file in this path does NOT exist')
            hide_menu()

    elif chosen_option == '2':

        chosen_audio_option = input(
            '\t press V & enter to use voice audio \t press S & enter to use sound audio\t press M & enter to use '
            'song audio\n')
        if chosen_audio_option == 'M' or chosen_audio_option == 'm':
            chosen_audio = 'C:\\Users\\*****\\PycharmProjects\\steganography\\song_audio.wav'

        elif chosen_audio_option == 'v' or chosen_audio_option == 'V':
            chosen_audio = 'C:\\Users\\*****\\PycharmProjects\\steganography\\voice_audio.wav'
        elif chosen_audio_option == 's' or chosen_audio_option == 'S':
            chosen_audio = 'C:\\Users\\*****\\Desktop\\audio\\hracka_pes.wav'

        else:
            print('No such a option !')
            hide_menu()

    else:
        print('I don\'t think we have such a option')
        hide_menu()


def reveal_menu():
    global chosen_audio

    chosen_audio = ''
    print(' \nCHOOSE YOUR AUDIO FILE! ')
    chosen_option = int(
        input('\t press 1 & ENTER for your own audio path\n''\t press 2 & ENTER for default audio file\n'))

    if chosen_option == 1:
        file_path = input('Enter a file path: ')

        if os.path.exists(file_path):
            print('The path is okay, file exists!')
            chosen_audio = file_path

        else:
            print('The specified file in this path does NOT exist')
            hide_menu()

    elif chosen_option == 2:
        pass


mode()
# menu()
# hide()

note - cannot use library for steganography

Hearing the noise in the modified_audio is the main problem


Solution

  • Your code is written to deal with wave files where sampwidth=1, but per your comment, the sampwidth of your file is 2. This means that your frame_bytes array is not an array of samples, it's an array of bytes in which every two bytes together form one sample. (And because nchannels is 1, there is one sample per frame, mono audio.) So when you changed the LSB of all the bytes, you were changing not only the LSB of each sample, but also a bit in the middle of it.

    You should convert this to an array of samples, and then change the samples LSBs. You can use array for this:

    import array
    
    samples = array.array('h', frame_bytes)  # signed 16-bit
    # ... modify the samples ...
    frames_modified = samples.tobytes()
    

    Of course, it would be best if your code checked beforehand if you're dealing with signed shorts (sampsize==2) or unsigned bytes (sampsize==1), and then handle them accordingly. You might do something like:

    samples = array.array('h' if sampsize == 2 else 'B', frame_bytes)
    for i, bit in enumerate(bits):
        samples[i] = samples[i] & -2 | bit
    

    (I have not tested this much)