chs.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. # conda activate VIP
  2. # pip install pycryptodome mmh3
  3. # -*- coding: utf-8 -*-
  4. """
  5. CHS -- Csv Hash Steganography
  6. Хеш-Стеганография в CSV файлах
  7. См. статью "Хеш-стеганография в DataSets. На этот раз быстрая": https://habrahabr.ru/post/339432/
  8. # python chs.py -m "text message" -i world-cities.csv -o stego.csv
  9. # Введите пароль: 123456789123456789
  10. # python chs.py -i stego.csv
  11. # Введите пароль: 123456789123456789
  12. # Извлечённое сообщение:'text message'
  13. Created by pavel on 05.10.17 17:21
  14. """
  15. import os
  16. import datetime
  17. import hashlib
  18. import mmh3
  19. import argparse
  20. from Crypto.Cipher import AES
  21. __author__ = 'pavel'
  22. __email__ = 'pavelmstu@stego.su'
  23. seed = 239239239239
  24. salt = 'bmstu20132017'
  25. ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
  26. def get_byte(line):
  27. return mmh3.hash_bytes(line, seed=seed)[0]
  28. def generate_source(path_csv):
  29. bytes_dict = {bytes([i])[0]:list() for i in range(256)}
  30. fr = open(path_csv, 'r')
  31. def get_value(byte_):
  32. nonlocal bytes_dict
  33. if len(bytes_dict[byte_]) >= 1:
  34. line_list = bytes_dict[byte_]
  35. line = line_list[0]
  36. line_list.pop(0)
  37. return line
  38. line = fr.readline()
  39. byte_line = get_byte(line)
  40. if byte_line == byte_:
  41. return line
  42. while True:
  43. bytes_dict[byte_line].append(line)
  44. # ###
  45. # TODO защита от того, что файл кончился
  46. line = fr.readline()
  47. byte_line = get_byte(line)
  48. if byte_line == byte_:
  49. return line
  50. else:
  51. continue
  52. def end():
  53. fr.close()
  54. header = fr.readline()
  55. return header, get_value, end
  56. def get_key(password):
  57. password = '{1}#{0}'.format(password, salt)
  58. return hashlib.sha256(password.encode('utf-8')).digest()
  59. def encrypt(message, password):
  60. """
  61. Шифрование
  62. :param message: текстовое сообщение
  63. :param password: текстовый пароль
  64. :return:
  65. bytes -- crypt_message
  66. """
  67. key = get_key(password)
  68. obj = AES.new(key, AES.MODE_ECB)
  69. message_body = message.encode('utf-8')
  70. while len(message_body) % 16 != 0:
  71. message_body += b'\0'
  72. crypt_message = obj.encrypt(message_body)
  73. return crypt_message
  74. def decrypt(crypt_message, password):
  75. """
  76. Расшифрование crypt_message по паролю password
  77. :param crypt_message: bytes
  78. :param password:
  79. :return:
  80. str -- message
  81. """
  82. key = get_key(password)
  83. obj = AES.new(key, AES.MODE_ECB)
  84. body_message = obj.decrypt(crypt_message)
  85. while body_message[-1] == 0:
  86. body_message = body_message[:-1]
  87. message = body_message.decode('utf-8')
  88. return message
  89. def stego_generate(
  90. path_csv_in,
  91. path_csv_out,
  92. message,
  93. password,
  94. ):
  95. """
  96. Функция, созда
  97. :param path_csv_in: входной CSV
  98. :param path_csv_out: выходной CSV
  99. :param message: сообщение для стегосообщения
  100. :param password: пароль для стегосообщения
  101. :return:
  102. """
  103. crypt_message = encrypt(message, password)
  104. with open(path_csv_out, 'w') as fw:
  105. header, get_value, end = generate_source(path_csv_in)
  106. fw.write(header)
  107. print("header:: '{0}'".format(header.replace('\n', '')))
  108. for byte_ in crypt_message:
  109. line = get_value(byte_)
  110. fw.write(line)
  111. print("{0} --> '{1}'".format(byte_, line.replace('\n', '')))
  112. end()
  113. def stego_extract(
  114. path_csv,
  115. password,
  116. ):
  117. """
  118. Функция по извлечению полезной информации из CSV файла
  119. :param path_csv: выходной CSV от функции stego_generate
  120. :param password: пароль
  121. :return:
  122. str -- message
  123. """
  124. byte_list = list()
  125. with open(path_csv, 'r') as fr:
  126. fr.readline()
  127. for line in fr:
  128. byte_ = get_byte(line)
  129. byte_list.append(byte_)
  130. crypt_message = bytes(byte_list)
  131. message = decrypt(crypt_message, password)
  132. return message
  133. def __test():
  134. path_csv_in = os.path.join(ROOT_PATH, 'data', 'world-cities.csv')
  135. path_csv_out = os.path.join(ROOT_PATH, 'data', 'world-cities.stego.csv')
  136. message = """Как рано мог он лицемерить,
  137. Таить надежду, ревновать,
  138. Разуверять, заставить верить,
  139. Казаться мрачным, изнывать,
  140. Являться гордым и послушным,
  141. Внимательным иль равнодушным!
  142. Как томно был он молчалив,
  143. Как пламенно красноречив,
  144. В сердечных письмах как небрежен!
  145. Одним дыша, одно любя,
  146. Как он умел забыть себя!
  147. Как взор его был быстр и нежен,
  148. Стыдлив и дерзок, а порой
  149. Блистал послушною слезой!
  150. """
  151. password = """И всё же порядок вещей нелеп.
  152. Люди, плавящие металл,
  153. Ткущие ткани, пекущие хлеб,
  154. Кто-то бессовестно вас обокрал.
  155. Не только ваш труд, любовь, досуг -
  156. Украли пытливость открытых глаз;
  157. Набором истин кормя из рук,
  158. Умение мыслить украли у вас.
  159. """
  160. print("stego_generate ({0})".format(datetime.datetime.now()))
  161. stego_generate(
  162. path_csv_in,
  163. path_csv_out,
  164. message,
  165. password,
  166. )
  167. print("stego_extract ({0})".format(datetime.datetime.now()))
  168. message2 = stego_extract(path_csv_out, password)
  169. print('done! ({0})'.format(datetime.datetime.now()))
  170. print("Извлеченное сообщение:\n\n{0}".format(message2))
  171. def __test2():
  172. path_csv_out = os.path.join(ROOT_PATH, 'data', 'world-cities.stego.csv')
  173. password = """И всё же порядок вещей нелеп.
  174. Люди, плавящие металл,
  175. Ткущие ткани, пекущие хлеб,
  176. Кто-то бессовестно вас обокрал.
  177. Не только ваш труд, любовь, досуг -
  178. Украли пытливость открытых глаз;
  179. Набором истин кормя из рук,
  180. Умение мыслить украли у вас.
  181. """
  182. message2 = stego_extract(path_csv_out, password)
  183. print("message2::{0}".format(message2))
  184. def main(args):
  185. args = vars(args)
  186. if 'message' in args and args['message'] is not None:
  187. # режим генерирования
  188. message = args['message']
  189. path_csv_in = args['input']
  190. if not path_csv_in.startswith('/'):
  191. path_csv_in = os.path.join(ROOT_PATH, path_csv_in)
  192. path_csv_out = args['output']
  193. if not path_csv_out.startswith('/'):
  194. path_csv_out = os.path.join(ROOT_PATH, path_csv_out)
  195. password = input("Введите пароль: ")
  196. stego_generate(
  197. path_csv_in,
  198. path_csv_out,
  199. message,
  200. password,
  201. )
  202. pass
  203. else:
  204. # режим извлечения
  205. path_csv_in = args['input']
  206. password = input("Введите пароль: ")
  207. message = stego_extract(
  208. path_csv_in,
  209. password
  210. )
  211. print("Извлечённое сообщение:'{}'".format(message))
  212. pass
  213. if __name__ == u"__main__":
  214. print(u'Run chs {0}'.format(datetime.datetime.now()))
  215. # __test()
  216. # __test2()
  217. parser = argparse.ArgumentParser(
  218. description='CSV Hash Steganography',
  219. epilog='See more information:https://habrahabr.ru/post/339432/\nPavelMSTU <PavelMSTU@stego.su>'
  220. )
  221. parser.add_argument(
  222. '-m', '--message',
  223. help='Сообщение для внесения в генерируемый CSV. Если не введен -- режим извлечения.',
  224. )
  225. parser.add_argument(
  226. '-i', '--input',
  227. help='Входной CSV-файл донор (если есть флаг -m); файл CSV для извлечения сообщения (если не указан флаг -m)',
  228. required=True,
  229. )
  230. parser.add_argument(
  231. '-o', '--output',
  232. help="Выходной CSV-файл (если есть флаг -m); если не указан флаг -m, флаг игнорируется",
  233. # required=True,
  234. )
  235. args = parser.parse_args()
  236. main(args)