├── .idea
├── deployment.xml
├── markdown-navigator.xml
├── vcs.xml
└── webServers.xml
├── README.md
├── data
├── chef.mp4
├── recovered-text.txt
└── text-to-hide.txt
├── functions.py
├── images
├── Selection_032.png
└── Selection_033.png
└── main.py
/.idea/deployment.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/webServers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Caesar-Cipher-Video-Steganography(CCVS)
2 |
3 | CCVS is a simple program written in python to hide encrypted text using simple caesar cipher in video frames, the encryption algorithm could be easily changed because the cipher algorithm is loosly coupled.
4 |
5 | Dependencies :
6 |
7 | * ffmpeg
8 | * opencv
9 | * PIL
10 | * pyfiglet
11 |
12 | Usage :
13 |
14 | * write your plain text in text-to-hide.txt in data folder
15 |
16 | ```bash
17 | python main.py
18 |
19 | ```
20 | you'll be greet with the main menu to encrypt the plain text and hide it in the video or decrypt and retreive the plain text from the video
21 |
22 | 
23 |
24 | 
25 |
26 | For decryption plain text will be put in recovered-text.txt in data folder
27 |
28 | Warning :
29 | * For encryption the video will be converted into raw .mov video to make sure data in the video won't change after re encoding and decryption, and make sure you got enough space (For comparison : 2 minute of 720p video could result in 2GB raw .mov video)
30 | * temp folder will be created to dump temporary extracted frame , audio, and video.
31 | * the temp foldder will be created in case of extraction either decryption
32 |
--------------------------------------------------------------------------------
/data/chef.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r9ht/Caesar-Cipher-Video-Steganography/a51dae6259192a0fb943215a6c7259a48c574c13/data/chef.mp4
--------------------------------------------------------------------------------
/data/recovered-text.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r9ht/Caesar-Cipher-Video-Steganography/a51dae6259192a0fb943215a6c7259a48c574c13/data/recovered-text.txt
--------------------------------------------------------------------------------
/data/text-to-hide.txt:
--------------------------------------------------------------------------------
1 |
2 | Shin Nishimura – Fukafunkacid (SEGO ‘Miyabimania’ Remix), Plus Tokyo, 2008
3 | Da Others – Viva La Vida! (SEGO ‘Mi Vida Loca’ Remix), Pilot6 Recordings, 2009
4 | Mehdi D Vs. TheCrosh – Roda (SEGO ‘Roda Gila’ Remix), Cutz, 2009
5 | Tarot – Substance (SEGO Remix), TKC Music, 2009
6 | Alejandro Roman – Un Segundo De Tu Vida (SEGO Remix), Cutz, 2009
7 | Alejandro Roman – El Mundo Del Infinito (SEGO Remix), Cutz, 2009
8 | Andrea Saenz & Sebastian Reza – Sevilla! (SEGO Remix), Nine Records, 2009
9 | Tatsu Mihara – Comet (SEGO ‘Minimal Object’ Remix), Plus Tokyo, 2009
10 | Mhonoral – Voison (SEGO ‘Deus Ex Machina’ Remix), Plus Tokyo, 2009
11 | J.NO – No More Breath (SEGO Remix), BDivision, 2009
12 | Timmo – Shumminal (SEGO ‘Gitar Karatan’ Remix + Dub), BDivision, 2009
13 | Mario Roberti – Slave (Sego Birahi Tinggi Remix), BDivision, 2009
14 | Simone Barbieri Viale – Sunset (SEGO Remix), Cutz, 2009
15 | Simone Barbieri Viale – City Jungle (SEGO Remix), Cutz, 2009
16 | Diego Poblets – Massive Shock (SEGO Remix), Cutz, 2009
17 | Baramuda & Ginkel – Do It Wrong (Sego ‘Hit The Brick Wall’ Remix), Cutz, 2009
18 | Ilya Mosolov, Spacebird (SEGO Re-Busted), Cold Busted, 2009
19 | Ilya Mosolov, Light of Paradise (SEGO Re-Busted), Cold Busted, 2009
20 | Ilya Mosolov, Pax (SEGO Re-Busted), Cold Busted, 2009
21 | So Hattori, Tarot – No Limit (SEGO ‘Nambah Dua’ Remix), TKC Music, 2009
22 | Grunjah – Tighten Your Wings (SEGO ‘Too Tight’ Remix), Quimika, 2009
23 | Marlon D & Pete Lopez – She’s Obsessed (SEGO Remix), TKC Music, 2009
24 | So Hattori & Tarot – No Limit (SEGO ‘Nambah Satu’ Remix), TKC Music, 2010
25 | Shin Nishimura – Phycedelic Technelic (SEGO Remix), Plus Tokyo, 2010
26 | SEGO – Magic Buffer (SEGO Remix), TKC Music, 2010
27 |
--------------------------------------------------------------------------------
/functions.py:
--------------------------------------------------------------------------------
1 | from PIL import Image
2 | import shutil,cv2,os
3 |
4 | # references :
5 | #
6 | # https://www.daniweb.com/programming/software-development/code/485063/hide-private-message-in-an-image-python
7 | # http://zulko.github.io/blog/2013/09/27/read-and-write-video-frames-in-python-using-ffmpeg/
8 | # http://tsaith.github.io/combine-images-into-a-video-with-python-3-and-opencv-3.html
9 |
10 | # Fauzanil Zaki , 2017
11 |
12 | def frame_extract(video):
13 | temp_folder = 'temp'
14 | try:
15 | os.mkdir(temp_folder)
16 | except OSError:
17 | remove(temp_folder)
18 | os.mkdir(temp_folder)
19 |
20 | vidcap = cv2.VideoCapture("data/"+str(video))
21 | count = 0
22 |
23 | while True:
24 | success, image = vidcap.read()
25 | if not success:
26 | break
27 | cv2.imwrite(os.path.join(temp_folder, "{:d}.png".format(count)), image)
28 | count += 1
29 |
30 |
31 | def remove(path):
32 | """ param could either be relative or absolute. """
33 | if os.path.isfile(path):
34 | os.remove(path) # remove the file
35 | elif os.path.isdir(path):
36 | shutil.rmtree(path) # remove dir and all contains
37 | else:
38 | raise ValueError("file {} is not a file or dir.".format(path))
39 |
40 |
41 |
42 | def split2len(s, n):
43 | def _f(s, n):
44 | while s:
45 | yield s[:n]
46 | s = s[n:]
47 | return list(_f(s, n))
48 |
49 |
50 |
51 |
52 | def caesar_ascii(char,mode,n):
53 | if mode == "enc" :
54 | ascii = ord(char)
55 | return chr((ascii + n) % 128)
56 | elif mode == "dec" :
57 | ascii = ord(char)
58 | return chr((ascii - n) % 128)
59 |
60 |
61 | def encode_frame(frame_dir,text_to_hide,caesarn):
62 |
63 |
64 |
65 | # open the text file
66 |
67 | text_to_hide_open = open(text_to_hide, "r")
68 | text_to_hide = repr(text_to_hide_open.read())
69 |
70 | # split text to max 255 char each
71 |
72 | text_to_hide_chopped = split2len(text_to_hide,255)
73 |
74 | for text in text_to_hide_chopped:
75 | length = len(text)
76 | chopped_text_index = text_to_hide_chopped.index(text)
77 | frame = Image.open(str(frame_dir) +"/" + str(chopped_text_index+1) + ".png")
78 |
79 | if frame.mode != "RGB":
80 | print("Source frame must be in RGB format")
81 | return False
82 |
83 | # use copy of the file
84 |
85 | encoded = frame.copy()
86 | width, height = frame.size
87 |
88 | index = 0
89 | a = object
90 | for row in range(height):
91 | for col in range(width):
92 | r,g,b = frame.getpixel((col,row))
93 |
94 | # first value is length of the message per frame
95 | if row == 0 and col == 0 and index < length:
96 | asc = length
97 | if text_to_hide_chopped.index(text) == 0 :
98 | total_encoded_frame = len(text_to_hide_chopped)
99 | else:
100 | total_encoded_frame = g
101 | elif index <= length:
102 | c = text[index -1]
103 | # put the encypted character into ascii value
104 | asc = ord(caesar_ascii(c,"enc",caesarn))
105 | total_encoded_frame = g
106 | else:
107 | asc = r
108 | total_encoded_frame = g
109 | encoded.putpixel((col,row),(asc,total_encoded_frame,b))
110 | index += 1
111 | if encoded:
112 | encoded.save(str(frame_dir)+"/"+str(chopped_text_index+1) + ".png",compress_level=0)
113 |
114 | def decode_frame(frame_dir,caesarn):
115 |
116 | #take the first frame to get width, height, and total encoded frame
117 |
118 | # first_frame = Image.open(str(frame_dir) + "/0.jpg")
119 | first_frame = Image.open(str(frame_dir)+ "/" + "1.png")
120 | r,g,b = first_frame.getpixel((0,0))
121 | total_encoded_frame = g
122 | msg = ""
123 | for i in range (1,total_encoded_frame+1):
124 | frame = Image.open(str(frame_dir) + "/" + str(i) + ".png")
125 | width, height = frame.size
126 | index = 0
127 | for row in range(height):
128 | for col in range(width):
129 | try :
130 | r,g,b = frame.getpixel((col,row))
131 | except ValueError:
132 |
133 | # for some ong a(transparancy) is needed
134 | r, g, b, a = frame.getpixel((col, row))
135 | if row == 0 and col == 0:
136 | length = r
137 | elif index <= length:
138 | # put the decrypted character into string
139 | msg += caesar_ascii(chr(r),"dec",caesarn)
140 | index +=1
141 | #remove the first and the last quote
142 | msg = msg[1:-1]
143 | recovered_txt = open("data/recovered-text.txt", "w")
144 | recovered_txt.write(str(msg.decode('string_escape')))
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/images/Selection_032.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r9ht/Caesar-Cipher-Video-Steganography/a51dae6259192a0fb943215a6c7259a48c574c13/images/Selection_032.png
--------------------------------------------------------------------------------
/images/Selection_033.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/r9ht/Caesar-Cipher-Video-Steganography/a51dae6259192a0fb943215a6c7259a48c574c13/images/Selection_033.png
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from pyfiglet import Figlet
2 | from functions import *
3 | from subprocess import call,STDOUT
4 | import os
5 |
6 | # Fauzanil Zaki , 2017
7 | # feel free to use
8 |
9 | if __name__ == '__main__':
10 |
11 | # cool boi
12 |
13 | f = Figlet(font='slant')
14 | print(f.renderText("CCVS"))
15 | print("CaesarCipherVideoSteganography")
16 | print("")
17 | # print("By : ")
18 | # print("")
19 | # print("===Fauzanil Zaki===")
20 | # print("====Galih Dea P.===")
21 | # print("====Johan Eko P.===")
22 | # print("===Wiladhianty Y.==")
23 | # print("")
24 |
25 | print("Menu :")
26 | print("")
27 | print("(a) Encypt & Merge into Video")
28 | print("(b) Decrypt & Get the plain text")
29 | print("-----------------------")
30 | choice = raw_input("(!) Choose option : ")
31 |
32 | if choice == "a":
33 | # refresh terminal
34 | call(["clear"])
35 |
36 | print(f.renderText("Encrypt"))
37 | print("----------------------------------------")
38 | file_name = raw_input("(1) Video file name in the data folder ? : ")
39 |
40 | try:
41 | caesarn = int(raw_input("(2) Caesar cypher n value ? : "))
42 | except ValueError:
43 | print("-----------------------")
44 | print("(!) n is not an integer ")
45 | exit()
46 |
47 | try:
48 | open("data/" + file_name)
49 | except IOError:
50 | print("-----------------------")
51 | print("(!) File not found ")
52 | exit()
53 |
54 | print("-----------------------")
55 | print("(-) Extracting Frame(s)")
56 | frame_extract(str(file_name))
57 | print("(-) Extracting audio")
58 | # using system call
59 | #ffmpeg -i data/chef.mp4 -q:a 0 -map a temp/audio.mp3 -y
60 | # 2>/dev/null for supressing the output from ffmpeg
61 | call(["ffmpeg", "-i", "data/" + str(file_name), "-q:a", "0", "-map", "a", "temp/audio.mp3", "-y"],stdout=open(os.devnull, "w"), stderr=STDOUT)
62 | # useless
63 | print("(-) Reading text-to-hide.txt")
64 | print("(-) Encrypting & appending string into frame(s) ")
65 | encode_frame("temp", "data/text-to-hide.txt", caesarn)
66 | print("(-) Merging frame(s) ")
67 | #ffmpeg -i temp/%d.png -vcodec png data/enc-filename.mov
68 | call(["ffmpeg", "-i", "temp/%d.png" , "-vcodec", "png", "temp/video.mov", "-y"],stdout=open(os.devnull, "w"), stderr=STDOUT)
69 |
70 | print("(-) Optimizing encode & Merging audio ")
71 | # ffmpeg -i temp/temp-video.avi -i temp/audio.mp3 -codec copy data/enc-chef.mp4 -y
72 | call(["ffmpeg", "-i", "temp/video.mov", "-i", "temp/audio.mp3", "-codec", "copy","data/enc-" + str(file_name)+".mov", "-y"],stdout=open(os.devnull, "w"), stderr=STDOUT)
73 | print("(!) Success , output : enc-" + str(file_name)+".mov")
74 |
75 | elif choice == "b" :
76 | # refresh terminal
77 | call(["clear"])
78 |
79 | print(f.renderText("Decrypt"))
80 | print("----------------------------------------")
81 | file_name = raw_input("(1) Video file name in the data folder ? : ")
82 |
83 | try:
84 | caesarn = int(raw_input("(2) Caesar cypher n value ? : "))
85 | except ValueError:
86 | print("-----------------------")
87 | print("(!) n is not an integer ")
88 | exit()
89 |
90 | try:
91 | open("data/" + file_name)
92 | except IOError:
93 | print("-----------------------")
94 | print("(!) File not found ")
95 | exit()
96 |
97 | print("-----------------------")
98 | print("(-) Extracting Frame(s)")
99 | frame_extract(str(file_name))
100 | print("(-) Decrypting Frame(s)")
101 | decode_frame("temp",caesarn)
102 | #useless
103 | print("(-) Writing to recovered-text.txt")
104 | print("(!) Success")
105 |
106 |
107 | else:
108 | exit()
109 |
110 |
--------------------------------------------------------------------------------