Commit 8cfc1a2373c0fb2aa9b6fcbd6a4e795f3ac76320
1 parent
033d3b0d
Exists in
master
F5 half-finished.
Showing
13 changed files
with
278 additions
and
203 deletions
Show diff stats
jpegObj/__init__.pyc
No preview for this file type
msteg/StegBase.py
@@ -102,6 +102,10 @@ class StegBase(object): | @@ -102,6 +102,10 @@ class StegBase(object): | ||
102 | # recovering file size | 102 | # recovering file size |
103 | header_size = 4 * 8 | 103 | header_size = 4 * 8 |
104 | size_data, bits_cnt = self._raw_extract(steg_data, header_size) | 104 | size_data, bits_cnt = self._raw_extract(steg_data, header_size) |
105 | + if bits_cnt < header_size: | ||
106 | + raise Exception("Expected embedded size is %db but actually %db." % ( | ||
107 | + header_size, bits_cnt)) | ||
108 | + | ||
105 | size_data = bits2bytes(size_data) | 109 | size_data = bits2bytes(size_data) |
106 | size_hd = 0 | 110 | size_hd = 0 |
107 | for i in xrange(4): | 111 | for i in xrange(4): |
msteg/StegBase.pyc
No preview for this file type
msteg/steganography/F3.pyc
No preview for this file type
@@ -0,0 +1,101 @@ | @@ -0,0 +1,101 @@ | ||
1 | +__author__ = 'chunk' | ||
2 | + | ||
3 | +""" | ||
4 | +<p>This module implements a slight variant of the F4 steganography algorithm | ||
5 | +invented by Andreas Westfeld. It embeds a secret message in JPEG | ||
6 | +DCT coefficients.</p> | ||
7 | +It differs from F3 in that even negative and odd positive DCT | ||
8 | +coefficients represent a 1 and odd negative and even positive | ||
9 | +DCT coefficients represent a 0. It also supports permutative strattling | ||
10 | +which is not included in the original description of F4. | ||
11 | +""" | ||
12 | +import time | ||
13 | +import numpy as np | ||
14 | +from msteg.StegBase import StegBase | ||
15 | +from common import * | ||
16 | + | ||
17 | + | ||
18 | +class F4(StegBase): | ||
19 | + """ This module has two methods: <i>embed_raw_data</i> to embed data | ||
20 | + with the F3 algorithm and <i>extract_raw_data</i> to extract data | ||
21 | + which was embedded previously. """ | ||
22 | + | ||
23 | + def __init__(self): | ||
24 | + """ | ||
25 | + Constructor of the F3 class. | ||
26 | + """ | ||
27 | + StegBase.__init__(self) | ||
28 | + | ||
29 | + def embed_raw_data(self, src_cover, src_hidden, tgt_stego): | ||
30 | + """ This method embeds arbitrary data into a cover image. | ||
31 | + The cover image must be a JPEG. | ||
32 | + | ||
33 | + src_cover - A valid pathname to an image file which serves as cover image | ||
34 | + (the image which the secret image is embedded into). | ||
35 | + | ||
36 | + src_hidden - A valid pathname to an arbitrary file that is supposed to be | ||
37 | + embedded into the cover image. | ||
38 | + | ||
39 | + tgt_stego - Target pathname of the resulting stego image. You should save to a | ||
40 | + PNG or another lossless format, because many LSBs don't survive | ||
41 | + lossy compression. | ||
42 | + """ | ||
43 | + self.t0 = time.time() | ||
44 | + StegBase._post_embed_actions(self, src_cover, src_hidden, tgt_stego) | ||
45 | + | ||
46 | + def extract_raw_data(self, src_steg, tgt_hidden): | ||
47 | + """ This method extracts secret data from a stego image. It is | ||
48 | + (obviously) the inverse operation of embed_raw_data. | ||
49 | + | ||
50 | + src_stego - A valid pathname to an image file which serves as stego image. | ||
51 | + | ||
52 | + tgt_hidden - A pathname denoting where the extracted data should be saved to. | ||
53 | + """ | ||
54 | + self.t0 = time.time() | ||
55 | + StegBase._post_extract_actions(self, src_steg, tgt_hidden) | ||
56 | + | ||
57 | + def _raw_embed(self, cov_data, hid_data, status_begin=0): | ||
58 | + """ | ||
59 | + cov_data - 4-D numpy.int32 array | ||
60 | + hid_data - 1-D numpy.uint8 array | ||
61 | + """ | ||
62 | + hid_data = bytes2bits(hid_data) | ||
63 | + i = 0 | ||
64 | + cnt = -1 | ||
65 | + for x in np.nditer(cov_data, op_flags=['readwrite']): | ||
66 | + cnt = cnt + 1 | ||
67 | + if x == 0 or cnt % 64 == 0: continue | ||
68 | + | ||
69 | + m = (hid_data[i] & 1) | ||
70 | + if x > 0 and x & 1 != m: | ||
71 | + x[...] -= 1 | ||
72 | + elif x < 0 and x & 1 == m: | ||
73 | + x[...] += 1 | ||
74 | + if x == 0: continue | ||
75 | + i += 1 | ||
76 | + if i == hid_data.size: break | ||
77 | + | ||
78 | + return cov_data | ||
79 | + | ||
80 | + def _raw_extract(self, steg_data, num_bits): | ||
81 | + """ | ||
82 | + Just a small helper function to extract hidden data. | ||
83 | + """ | ||
84 | + hid_data = np.zeros(num_bits, np.uint8) | ||
85 | + j = 0 | ||
86 | + cnt = -1 | ||
87 | + for x in np.nditer(steg_data): | ||
88 | + cnt = cnt + 1 | ||
89 | + if x == 0 or cnt % 64 == 0: continue | ||
90 | + if j >= num_bits: break | ||
91 | + if x > 0: | ||
92 | + hid_data[j] = x & 1 | ||
93 | + else: | ||
94 | + hid_data[j] = (x & 1) ^ 1 | ||
95 | + | ||
96 | + j = j + 1 | ||
97 | + | ||
98 | + return hid_data | ||
99 | + | ||
100 | + def __str__(self): | ||
101 | + return "F4'" |
msteg/steganography/F4.py
@@ -74,6 +74,10 @@ class F4(StegBase): | @@ -74,6 +74,10 @@ class F4(StegBase): | ||
74 | # recovering file size | 74 | # recovering file size |
75 | header_size = 4 * 8 | 75 | header_size = 4 * 8 |
76 | size_data, bits_cnt = self._raw_extract(steg_data, header_size) | 76 | size_data, bits_cnt = self._raw_extract(steg_data, header_size) |
77 | + if bits_cnt < header_size: | ||
78 | + raise Exception("Expected embedded size is %db but actually %db." % ( | ||
79 | + header_size, bits_cnt)) | ||
80 | + | ||
77 | size_data = bits2bytes(size_data) | 81 | size_data = bits2bytes(size_data) |
78 | 82 | ||
79 | size_hd = 0 | 83 | size_hd = 0 |
@@ -120,6 +124,7 @@ class F4(StegBase): | @@ -120,6 +124,7 @@ class F4(StegBase): | ||
120 | if x == 0: continue | 124 | if x == 0: continue |
121 | i += 1 | 125 | i += 1 |
122 | if i == hid_data.size: break | 126 | if i == hid_data.size: break |
127 | + | ||
123 | return cov_data, i | 128 | return cov_data, i |
124 | 129 | ||
125 | def _raw_extract(self, steg_data, num_bits): | 130 | def _raw_extract(self, steg_data, num_bits): |
@@ -127,6 +132,7 @@ class F4(StegBase): | @@ -127,6 +132,7 @@ class F4(StegBase): | ||
127 | Just a small helper function to extract hidden data. | 132 | Just a small helper function to extract hidden data. |
128 | steg_data - 1-D numpy.int16 array (permunated) | 133 | steg_data - 1-D numpy.int16 array (permunated) |
129 | """ | 134 | """ |
135 | + | ||
130 | hid_data = np.zeros(num_bits, np.uint8) | 136 | hid_data = np.zeros(num_bits, np.uint8) |
131 | j = 0 | 137 | j = 0 |
132 | for x in steg_data: | 138 | for x in steg_data: |
msteg/steganography/F4.pyc
No preview for this file type
msteg/steganography/F5.py
1 | +__author__ = 'chunk' | ||
2 | + | ||
1 | """ | 3 | """ |
2 | <p>This module implements the rather sophisticated F5 algorithm which was | 4 | <p>This module implements the rather sophisticated F5 algorithm which was |
3 | invented by Andreas Westfeld.</p> | 5 | invented by Andreas Westfeld.</p> |
@@ -35,118 +37,134 @@ class F5(StegBase): | @@ -35,118 +37,134 @@ class F5(StegBase): | ||
35 | with the F5 algorithm and <i>extract_raw_data</i> to extract data | 37 | with the F5 algorithm and <i>extract_raw_data</i> to extract data |
36 | which was embedded previously. """ | 38 | which was embedded previously. """ |
37 | 39 | ||
38 | - def __init__(self, key=sample_key): | 40 | + def __init__(self, key=sample_key, k=None): |
39 | """ | 41 | """ |
40 | Constructor of the F5 class. | 42 | Constructor of the F5 class. |
41 | """ | 43 | """ |
42 | StegBase.__init__(self, key) | 44 | StegBase.__init__(self, key) |
43 | self._embed_fun = None | 45 | self._embed_fun = None |
44 | self.default_embedding = True | 46 | self.default_embedding = True |
45 | - self.steg_ind = -1 | ||
46 | - self.excess_bits = None | 47 | + |
47 | # needed because k is embedded separately | 48 | # needed because k is embedded separately |
48 | - self.cov_ind = -1 | ||
49 | - self.k_coeff = -1 | ||
50 | - | ||
51 | - @describe_annotate_convert((None, None, ident), | ||
52 | - ("cover image", ImagePath, str), | ||
53 | - ("hidden data", FilePath, str), | ||
54 | - ("stego image", NewFilePath, str), | ||
55 | - ("seed", int, int), | ||
56 | - ("embedding behavior", | ||
57 | - ['Default', 'F3', 'JSteg'], str)) | ||
58 | - def embed_raw_data(self, src_cover, src_hidden, tgt_stego, seed, | ||
59 | - embed_fun): | ||
60 | - """<p>This method embeds arbitrary data into a cover image. | ||
61 | - The cover image must be a JPEG.</p> | ||
62 | - | ||
63 | - <p>Parameters: | ||
64 | - <ol> | ||
65 | - <li><pre>src_cover</pre> | ||
66 | - A valid pathname to an image file which serves as cover image | ||
67 | - (the image which the secret image is embedded into).</li> | ||
68 | - | ||
69 | - <li><pre>src_hidden</pre> | ||
70 | - A valid pathname to an arbitrary file that is supposed to be | ||
71 | - embedded into the cover image.</li> | ||
72 | - | ||
73 | - <li><pre>tgt_stego</pre> | ||
74 | - Target pathname of the resulting stego image. You should save to | ||
75 | - a PNG or another lossless format, because many LSBs don't survive | ||
76 | - lossy compression.</li> | ||
77 | - | ||
78 | - <li><pre>seed</pre> | ||
79 | - A seed for the random number generator that is responsible scattering | ||
80 | - the secret data within the cover image.</li> | ||
81 | - | ||
82 | - <li><pre>param embed_fun</pre> | 49 | + self.k_coeff = k |
50 | + | ||
51 | + | ||
52 | + def _get_cov_data(self, img_path): | ||
53 | + """ | ||
54 | + Returns DCT coefficients of the cover image. | ||
55 | + """ | ||
56 | + self.cov_jpeg = jpegObj.Jpeg(img_path, key=self.key) | ||
57 | + | ||
58 | + cov_data = self.cov_jpeg.getsignal(channel='Y') | ||
59 | + self.cov_data = np.array(cov_data, dtype=np.int16) | ||
60 | + return self.cov_data | ||
61 | + | ||
62 | + def embed_raw_data(self, src_cover, src_hidden, tgt_stego, embed_fun='Default'): | ||
63 | + """This method embeds arbitrary data into a cover image. | ||
64 | + The cover image must be a JPEG. | ||
65 | + | ||
66 | + @param embed_fun: | ||
83 | Specifies which embedding function should be used. Must be one of | 67 | Specifies which embedding function should be used. Must be one of |
84 | 'Default', 'F3', 'Jsteg'. If 'Default' is selected, the algorithm uses | 68 | 'Default', 'F3', 'Jsteg'. If 'Default' is selected, the algorithm uses |
85 | the same behavior as Westfeld's implementation, i.e. decrementing | 69 | the same behavior as Westfeld's implementation, i.e. decrementing |
86 | absolute values for n > 1 (F3) and using F4 in the special case n = 1. | 70 | absolute values for n > 1 (F3) and using F4 in the special case n = 1. |
87 | - Selecting F3 or JSteg results in using that scheme for all n.</li> | ||
88 | - </ol> | ||
89 | - </p> | 71 | + Selecting F3 or JSteg results in using that scheme for all n. |
90 | """ | 72 | """ |
91 | self.t0 = time.time() | 73 | self.t0 = time.time() |
92 | - self.seed = seed | 74 | + |
93 | if embed_fun == 'F3': | 75 | if embed_fun == 'F3': |
94 | self._embed_fun = self._f3_embed | 76 | self._embed_fun = self._f3_embed |
95 | self.default_embedding = False | 77 | self.default_embedding = False |
96 | - elif embed_fun == 'JSteg': | 78 | + elif embed_fun == 'JSteg' or embed_fun == 'LSB': |
97 | self._embed_fun = self._jsteg_embed | 79 | self._embed_fun = self._jsteg_embed |
98 | self.default_embedding = False | 80 | self.default_embedding = False |
99 | - elif embed_fun == 'Default': | 81 | + else: |
100 | self._embed_fun = self._f3_embed | 82 | self._embed_fun = self._f3_embed |
101 | self.default_embedding = True | 83 | self.default_embedding = True |
102 | 84 | ||
103 | - self.cov_ind = -1 | ||
104 | - JPEGSteg._post_embed_actions(self, src_cover, src_hidden, tgt_stego) | 85 | + try: |
86 | + cov_data = self._get_cov_data(src_cover) | ||
87 | + hid_data = self._get_hid_data(src_hidden) | ||
88 | + # print hid_data.dtype,type(hid_data),hid_data.tolist() | ||
105 | 89 | ||
106 | - @describe_annotate_convert((None, None, ident), | ||
107 | - ("stego image", ImagePath, str), | ||
108 | - ("hidden data", NewFilePath, str), | ||
109 | - ("seed", int, int), | ||
110 | - ("embedding behavior", ['Default', 'F3/JSteg'], | ||
111 | - str)) | ||
112 | - def extract_raw_data(self, src_steg, tgt_hidden, seed, embed_fun): | ||
113 | - """<p>This method extracts secret data from a stego image. It is | ||
114 | - (obviously) the inverse operation of embed_raw_data.</p> | 90 | + cov_data, bits_cnt = self._raw_embed(cov_data, hid_data) |
115 | 91 | ||
116 | - <p>Parameters: | ||
117 | - <ol> | ||
118 | - <li><pre>src_stego</pre> | ||
119 | - A valid pathname to an image file which serves as stego image.</li> | 92 | + if bits_cnt < np.size(hid_data) * 8: |
93 | + raise Exception("Expected embedded size is %db but actually %db." % ( | ||
94 | + np.size(hid_data) * 8, bits_cnt)) | ||
120 | 95 | ||
121 | - <li><pre>tgt_hidden</pre> | ||
122 | - A pathname denoting where the extracted data should be saved to.</li> | 96 | + self.cov_jpeg.setsignal(cov_data, channel='Y') |
97 | + self.cov_jpeg.Jwrite(tgt_stego) | ||
123 | 98 | ||
124 | - <li><pre>param seed</pre> | ||
125 | - A seed for the random number generator that is responsible scattering | ||
126 | - the secret data within the cover image.</li> | 99 | + # size_cov = os.path.getsize(tgt_stego) |
100 | + size_cov = np.size(cov_data) / 8 | ||
101 | + size_embedded = np.size(hid_data) | ||
127 | 102 | ||
128 | - <li><pre>param embed_fun</pre> | ||
129 | - Specifies which embedding function should be used. Must be one of | ||
130 | - 'Default', 'F3', 'JSteg'. If 'Default' is selected, the algorithm uses | ||
131 | - the same behavior as Westfeld's implementation, i.e. decrementing | ||
132 | - absolute values for n > 1 (F3) and using F4 in the special case n = 1. | ||
133 | - Selecting F3 or JSteg results in using that scheme for all n.</li> | ||
134 | - </ol></pre> | ||
135 | - """ | 103 | + self._display_stats("embedded", size_cov, size_embedded, |
104 | + time.time() - self.t0) | ||
136 | 105 | ||
106 | + except TypeError as e: | ||
107 | + raise e | ||
108 | + except Exception as expt: | ||
109 | + print "Exception when embedding!" | ||
110 | + raise | ||
111 | + | ||
112 | + | ||
113 | + def extract_raw_data(self, src_steg, tgt_hidden, embed_fun='Default'): | ||
137 | self.t0 = time.time() | 114 | self.t0 = time.time() |
138 | - self.seed = seed | ||
139 | - self.steg_ind = -1 | ||
140 | - if embed_fun == 'F3/JSteg': | 115 | + |
116 | + if embed_fun == 'F3': | ||
117 | + self._embed_fun = self._f3_embed | ||
118 | + self.default_embedding = False | ||
119 | + elif embed_fun == 'JSteg' or embed_fun == 'LSB': | ||
120 | + self._embed_fun = self._jsteg_embed | ||
141 | self.default_embedding = False | 121 | self.default_embedding = False |
142 | - elif embed_fun == 'Default': | 122 | + else: |
123 | + self._embed_fun = self._f3_embed | ||
143 | self.default_embedding = True | 124 | self.default_embedding = True |
144 | 125 | ||
145 | - # excess bits occur when the size of extracted data is not a multiple | ||
146 | - # of k. if excess bits are available, they are prepended to hidden data | ||
147 | - self.excess_bits = None | 126 | + try: |
127 | + steg_data = self._get_cov_data(src_steg) | ||
128 | + # emb_size = os.path.getsize(src_steg) | ||
129 | + emb_size = np.size(steg_data) / 8 | ||
130 | + | ||
131 | + # recovering file size | ||
132 | + header_size = 4 * 8 | ||
133 | + size_data, bits_cnt = self._raw_extract(steg_data, header_size) | ||
134 | + | ||
135 | + if bits_cnt < header_size: | ||
136 | + raise Exception("Expected embedded size is %db but actually %db." % ( | ||
137 | + header_size, bits_cnt)) | ||
138 | + | ||
139 | + size_data = bits2bytes(size_data[:header_size]) | ||
140 | + print size_data | ||
141 | + | ||
142 | + size_hd = 0 | ||
143 | + for i in xrange(4): | ||
144 | + size_hd += size_data[i] * 256 ** i | ||
145 | + | ||
146 | + raw_size = size_hd * 8 | ||
147 | + | ||
148 | + if raw_size > np.size(steg_data): | ||
149 | + raise Exception("Supposed secret data too large for stego image.") | ||
150 | + | ||
151 | + hid_data, bits_cnt = self._raw_extract(steg_data, raw_size) | ||
152 | + | ||
153 | + if bits_cnt < raw_size: | ||
154 | + raise Exception("Expected embedded size is %db but actually %db." % ( | ||
155 | + raw_size, bits_cnt)) | ||
156 | + | ||
157 | + hid_data = bits2bytes(hid_data) | ||
158 | + # print hid_data.dtype,type(hid_data),hid_data.tolist() | ||
159 | + hid_data[4:].tofile(tgt_hidden) | ||
160 | + | ||
161 | + self._display_stats("extracted", emb_size, | ||
162 | + np.size(hid_data), | ||
163 | + time.time() - self.t0) | ||
164 | + except Exception as expt: | ||
165 | + print "Exception when extracting!" | ||
166 | + raise | ||
148 | 167 | ||
149 | - JPEGSteg._post_extract_actions(self, src_steg, tgt_hidden) | ||
150 | 168 | ||
151 | def _embed_k(self, cov_data, hid_data): | 169 | def _embed_k(self, cov_data, hid_data): |
152 | np.random.seed(self.seed) | 170 | np.random.seed(self.seed) |
@@ -218,153 +236,89 @@ class F5(StegBase): | @@ -218,153 +236,89 @@ class F5(StegBase): | ||
218 | 236 | ||
219 | def _jsteg_embed(self, cov_data, ind): | 237 | def _jsteg_embed(self, cov_data, ind): |
220 | m = 1 ^ (cov_data[ind] & 1) | 238 | m = 1 ^ (cov_data[ind] & 1) |
221 | - cov_data[ind] = (cov_data[ind] & 0xffffe) | m | 239 | + cov_data[ind] = (cov_data[ind] & 0xfffffffe) | m |
222 | 240 | ||
223 | - def _raw_embed(self, cov_data, hid_data, status_begin=0): | 241 | + def _raw_embed(self, cov_data, hid_data): |
224 | k = self.k_coeff | 242 | k = self.k_coeff |
225 | n = (1 << k) - 1 | 243 | n = (1 << k) - 1 |
244 | + | ||
226 | if n == 1 and self.default_embedding: | 245 | if n == 1 and self.default_embedding: |
227 | - # in case k = n = 1, Westfeld's implementation uses F4 for | ||
228 | - # embedding. Therefore, if 'default' embedding has been selected | ||
229 | - # we will do the same | ||
230 | - f4 = F4(self.ui, self.core) | ||
231 | - f4.seed = self.seed | ||
232 | - f4.dct_p = self.dct_p | ||
233 | - f4.cov_ind = self.cov_ind | ||
234 | - cov_data = f4._raw_embed(cov_data, hid_data, 30) | ||
235 | - return cov_data | ||
236 | - | ||
237 | - cov_ind = self.cov_ind # preventing RSI by writing 'self' less often | ||
238 | - hid_ind = 0 | ||
239 | - remaining_bits = hid_data.size | ||
240 | - hid_size = float(hid_data.size) | ||
241 | - dct_p = self.dct_p | ||
242 | - | ||
243 | - update_cnt = int(hid_size / (70.0 * k)) | ||
244 | - while remaining_bits > 0: | ||
245 | - if update_cnt == 0: | ||
246 | - self._set_progress(30 + int((( | ||
247 | - hid_size - remaining_bits) / hid_size) * 70)) | ||
248 | - update_cnt = int(hid_size / (70.0 * k)) | ||
249 | - update_cnt -= 1 | ||
250 | - msg_chunk_size = min(remaining_bits, k) | ||
251 | - msg_chunk = np.zeros(k, np.int8) | ||
252 | - cov_chunk = np.zeros(n, np.int32) | ||
253 | - msg_chunk[0:msg_chunk_size] = hid_data[hid_ind:hid_ind + | ||
254 | - msg_chunk_size] | ||
255 | - hid_ind += k | ||
256 | - | ||
257 | - # get n DCT coefficients | ||
258 | - for i in xrange(n): | ||
259 | - cov_ind += 1 | ||
260 | - while cov_data[dct_p[cov_ind]] == 0 \ | ||
261 | - or dct_p[cov_ind] % 64 == 0: | ||
262 | - cov_ind += 1 | ||
263 | - cov_chunk[i] = dct_p[cov_ind] | 246 | + # in case k = n = 1, Westfeld's implementation uses F4 for embedding. |
247 | + f4 = F4(key=self.key) | ||
248 | + return f4._raw_embed(cov_data, hid_data) | ||
249 | + | ||
250 | + hid_data = bytes2bits(hid_data) | ||
251 | + if len(hid_data) % k != 0: | ||
252 | + hid_data = list(hid_data) + [0 for x in range(k - len(hid_data) % k)] | ||
253 | + | ||
254 | + ind_nonzero = np.nonzero(cov_data)[0] | ||
255 | + | ||
256 | + if np.size(ind_nonzero) * k < len(hid_data) * n: | ||
257 | + raise Exception("Supposed secret data too large for stego image.") | ||
258 | + | ||
259 | + ind_cov = 0 | ||
260 | + for ind_hid in range(0, len(hid_data), k): | ||
261 | + msg_chunk = hid_data[ind_hid:ind_hid + k] | ||
262 | + cov_chunk = ind_nonzero[ind_cov:ind_cov + n] | ||
263 | + ind_cov += n | ||
264 | 264 | ||
265 | success = False | 265 | success = False |
266 | - while not success: # loop necessary because of shrinkage | 266 | + while not success: |
267 | h = 0 | 267 | h = 0 |
268 | for i in xrange(n): | 268 | for i in xrange(n): |
269 | h ^= ((cov_data[cov_chunk[i]] & 1) * (i + 1)) | 269 | h ^= ((cov_data[cov_chunk[i]] & 1) * (i + 1)) |
270 | scalar_x = 0 | 270 | scalar_x = 0 |
271 | for i in xrange(k): | 271 | for i in xrange(k): |
272 | - scalar_x = (scalar_x << 1) + msg_chunk[i] | 272 | + scalar_x = (scalar_x << 1) + msg_chunk[ |
273 | + i] # N.B. hid_data[0]:high (that is x2), hid_data[1]:low (that is x1) | ||
273 | s = scalar_x ^ h | 274 | s = scalar_x ^ h |
274 | if s != 0: | 275 | if s != 0: |
275 | self._embed_fun(cov_data, cov_chunk[s - 1]) | 276 | self._embed_fun(cov_data, cov_chunk[s - 1]) |
276 | else: | 277 | else: |
277 | break | 278 | break |
278 | 279 | ||
279 | - if cov_data[cov_chunk[s - 1]] == 0: # test for shrinkage | ||
280 | - cov_chunk[s - 1:-1] = cov_chunk[s:] # adjusting | ||
281 | - cov_ind += 1 | ||
282 | - while cov_data[dct_p[cov_ind]] == 0 or \ | ||
283 | - dct_p[cov_ind] % 64 == 0: | ||
284 | - cov_ind += 1 | ||
285 | - cov_chunk[n - 1] = dct_p[cov_ind] | 280 | + if cov_data[cov_chunk[s - 1]] == 0: # shrinkage |
281 | + cov_chunk[s - 1:-1] = cov_chunk[s:] | ||
282 | + cov_chunk[-1] = ind_nonzero[ind_cov] | ||
283 | + ind_cov += 1 | ||
286 | else: | 284 | else: |
287 | success = True | 285 | success = True |
288 | 286 | ||
289 | - remaining_bits -= k | ||
290 | - | ||
291 | - self.k_coeff = -1 # prevent k being read from this instance | ||
292 | - return cov_data | 287 | + return cov_data, ind_hid + k |
293 | 288 | ||
294 | - def _raw_extract(self, num_bits): | 289 | + def _raw_extract(self, steg_data, num_bits): |
295 | k = self.k_coeff | 290 | k = self.k_coeff |
296 | n = (1 << k) - 1 | 291 | n = (1 << k) - 1 |
297 | - if self.is_header == None: | ||
298 | - self.is_header = True | ||
299 | - if n == 1 and self.default_embedding: | ||
300 | - f4 = F4(self.ui, self.core) | ||
301 | - f4.seed = self.seed | ||
302 | - f4.dct_p = self.dct_p | ||
303 | - f4.steg_data = self.steg_data | ||
304 | - f4.is_header = self.is_header | ||
305 | - f4.steg_ind = self.steg_ind | ||
306 | - hid_data = f4._raw_extract(num_bits) | ||
307 | - self.steg_ind = f4.steg_ind | ||
308 | - self.is_header = False | ||
309 | - return hid_data | ||
310 | - remaining_bits = num_bits | ||
311 | - hid_data = np.zeros(num_bits, np.uint8) | ||
312 | - hid_ind = 0 | ||
313 | - | ||
314 | - dct_p = self.dct_p | ||
315 | - | ||
316 | - is_header = False # signals whether or not extracting header | ||
317 | - | ||
318 | - if self.excess_bits != None: | ||
319 | - hid_data[hid_ind:hid_ind + self.excess_bits.size] = \ | ||
320 | - self.excess_bits | ||
321 | - hid_ind += self.excess_bits.size | ||
322 | - remaining_bits -= self.excess_bits.size | ||
323 | - | ||
324 | - curr_chunk = np.zeros(k, np.uint8) | ||
325 | - | ||
326 | - update_cnt = int(num_bits / (100.0 * k)) | ||
327 | - | ||
328 | - while remaining_bits > 0: | ||
329 | 292 | ||
330 | - if update_cnt == 0 and not is_header: | ||
331 | - self._set_progress(int(((float(num_bits) \ | ||
332 | - - remaining_bits) / num_bits) * 100)) | ||
333 | - update_cnt = int(num_bits / (100.0 * k)) | 293 | + if n == 1 and self.default_embedding: |
294 | + f4 = F4(key=self.key) | ||
295 | + return f4._raw_extract(steg_data, num_bits) | ||
334 | 296 | ||
335 | - update_cnt -= 1 | 297 | + num_bits_ceil = num_bits |
298 | + if num_bits % k != 0: | ||
299 | + num_bits_ceil = k * (num_bits / k + 1) | ||
336 | 300 | ||
337 | - steg_chunk = [0 for i in xrange(n)] | ||
338 | - for i in xrange(n): | ||
339 | - self.steg_ind += 1 | ||
340 | - while self.steg_data[dct_p[self.steg_ind]] == 0 or \ | ||
341 | - dct_p[self.steg_ind] % 64 == 0: | ||
342 | - self.steg_ind += 1 | ||
343 | - steg_chunk[i] = self.steg_data[dct_p[self.steg_ind]] | 301 | + hid_data = np.zeros(num_bits_ceil, np.uint8) |
302 | + curr_chunk = np.zeros(k, np.uint8) | ||
303 | + steg_data = steg_data[np.nonzero(steg_data)] | ||
304 | + ind_hid = 0 | ||
305 | + for ind_cov in range(0, len(steg_data), n): | ||
306 | + steg_chunk = steg_data[ind_cov:ind_cov + n] | ||
344 | 307 | ||
345 | h = 0 # hash value | 308 | h = 0 # hash value |
346 | for i in xrange(n): | 309 | for i in xrange(n): |
347 | h ^= ((steg_chunk[i] & 1) * (i + 1)) | 310 | h ^= ((steg_chunk[i] & 1) * (i + 1)) |
348 | 311 | ||
349 | for i in xrange(k): | 312 | for i in xrange(k): |
350 | - curr_chunk[k - i - 1] = h % 2 | ||
351 | - h /= 2 | ||
352 | - | ||
353 | - l = min(k, remaining_bits) | ||
354 | - for i in xrange(l): | ||
355 | - hid_data[hid_ind] = curr_chunk[i] | ||
356 | - hid_ind += 1 | 313 | + curr_chunk[k - i - 1] = h & 1 # N.B. hid_data[0]:high (that is x2), hid_data[1]:low (that is x1) |
314 | + h >>= 1 | ||
357 | 315 | ||
358 | - # save excess bits (for later calls) | ||
359 | - if k > remaining_bits: | ||
360 | - self.excess_bits = curr_chunk[remaining_bits:] | ||
361 | - else: | ||
362 | - self.excess_bits = None | 316 | + hid_data[ind_hid:ind_hid + k] = curr_chunk[0:k] |
317 | + ind_hid += k | ||
363 | 318 | ||
364 | - remaining_bits -= k | 319 | + if ind_hid >= num_bits_ceil: break |
365 | 320 | ||
366 | - self.is_header = False | ||
367 | - return hid_data | 321 | + return hid_data, num_bits_ceil |
368 | 322 | ||
369 | def __str__(self): | 323 | def __str__(self): |
370 | return 'F5' | 324 | return 'F5' |
msteg/steganography/F5.pyc
No preview for this file type
msteg/steganography/LSB.pyc
No preview for this file type
res/steged.jpg
test_jpeg.py
@@ -17,7 +17,9 @@ sample = [[7, 12, 14, -12, 1, 0, -1, 0], | @@ -17,7 +17,9 @@ sample = [[7, 12, 14, -12, 1, 0, -1, 0], | ||
17 | [0, 0, 0, 0, 0, 0, 0, 0], | 17 | [0, 0, 0, 0, 0, 0, 0, 0], |
18 | [0, 0, 0, 0, 0, 0, 0, 0]] | 18 | [0, 0, 0, 0, 0, 0, 0, 0]] |
19 | 19 | ||
20 | -sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, 5908L, 59816L, 56765L] | 20 | +sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, |
21 | + 5908L, 59816L, 56765L] | ||
22 | + | ||
21 | 23 | ||
22 | def diffblock(c1, c2): | 24 | def diffblock(c1, c2): |
23 | diff = False | 25 | diff = False |
@@ -32,6 +34,7 @@ def diffblock(c1, c2): | @@ -32,6 +34,7 @@ def diffblock(c1, c2): | ||
32 | 34 | ||
33 | def diffblocks(a, b): | 35 | def diffblocks(a, b): |
34 | diff = False | 36 | diff = False |
37 | + cnt = 0 | ||
35 | for comp in range(a.image_components): | 38 | for comp in range(a.image_components): |
36 | xmax, ymax = a.Jgetcompdim(comp) | 39 | xmax, ymax = a.Jgetcompdim(comp) |
37 | for y in range(ymax): | 40 | for y in range(ymax): |
@@ -39,7 +42,8 @@ def diffblocks(a, b): | @@ -39,7 +42,8 @@ def diffblocks(a, b): | ||
39 | if a.Jgetblock(x, y, comp) != b.Jgetblock(x, y, comp): | 42 | if a.Jgetblock(x, y, comp) != b.Jgetblock(x, y, comp): |
40 | print("blocks({},{}) in component {} not match".format(y, x, comp)) | 43 | print("blocks({},{}) in component {} not match".format(y, x, comp)) |
41 | diff = True | 44 | diff = True |
42 | - return diff | 45 | + cnt += 1 |
46 | + return diff, cnt | ||
43 | 47 | ||
44 | 48 | ||
45 | def test_setblocks(): | 49 | def test_setblocks(): |
@@ -131,17 +135,18 @@ def test_rawfile(): | @@ -131,17 +135,18 @@ def test_rawfile(): | ||
131 | raw[i] = raw_size % 256 | 135 | raw[i] = raw_size % 256 |
132 | raw_size /= 256 | 136 | raw_size /= 256 |
133 | raw = np.array(raw) | 137 | raw = np.array(raw) |
134 | - print raw.shape,raw | 138 | + print raw.shape, raw |
135 | # print raw.size | 139 | # print raw.size |
136 | # print bytes2bits(raw) | 140 | # print bytes2bits(raw) |
137 | 141 | ||
142 | + | ||
138 | def test_bitbyte(): | 143 | def test_bitbyte(): |
139 | timer.mark() | 144 | timer.mark() |
140 | raw = np.fromfile("res/test4.jpg", np.uint8) | 145 | raw = np.fromfile("res/test4.jpg", np.uint8) |
141 | timer.report() | 146 | timer.report() |
142 | print raw | 147 | print raw |
143 | 148 | ||
144 | - bitsraw = bytes2bits(raw) | 149 | + bitsraw = bytes2bits(raw) |
145 | # bitsraw = bitsraw[:24] | 150 | # bitsraw = bitsraw[:24] |
146 | timer.report() | 151 | timer.report() |
147 | print bitsraw | 152 | print bitsraw |
@@ -150,6 +155,7 @@ def test_bitbyte(): | @@ -150,6 +155,7 @@ def test_bitbyte(): | ||
150 | timer.report() | 155 | timer.report() |
151 | print bytesraw | 156 | print bytesraw |
152 | 157 | ||
158 | + | ||
153 | def test_iter(): | 159 | def test_iter(): |
154 | imb = jpegObj.Jpeg("res/test4.jpg") | 160 | imb = jpegObj.Jpeg("res/test4.jpg") |
155 | blocks = imb.getCoefBlocks(channel='Y') | 161 | blocks = imb.getCoefBlocks(channel='Y') |
@@ -186,12 +192,12 @@ if __name__ == '__main__': | @@ -186,12 +192,12 @@ if __name__ == '__main__': | ||
186 | 192 | ||
187 | # test_bitbyte() | 193 | # test_bitbyte() |
188 | 194 | ||
189 | - ima = jpegObj.Jpeg("res/test3.jpg",key=sample_key) | 195 | + ima = jpegObj.Jpeg("res/test3.jpg", key=sample_key) |
190 | # imb = jpegObj.Jpeg("res/new.jpg",key=sample_key) | 196 | # imb = jpegObj.Jpeg("res/new.jpg",key=sample_key) |
191 | - imc = jpegObj.Jpeg("res/steged.jpg",key=sample_key) | 197 | + imc = jpegObj.Jpeg("res/steged.jpg", key=sample_key) |
192 | print ima.Jgetcompdim(0) | 198 | print ima.Jgetcompdim(0) |
193 | - print ima.getkey(),imc.getkey() | ||
194 | - diffblocks(ima, imc) | 199 | + print ima.getkey(), imc.getkey() |
200 | + print diffblocks(ima, imc) | ||
195 | 201 | ||
196 | # c1 = ima.getCoefBlocks() | 202 | # c1 = ima.getCoefBlocks() |
197 | # c2 = imb.getCoefBlocks() | 203 | # c2 = imb.getCoefBlocks() |
test_steg.py
@@ -7,7 +7,7 @@ import pylab as plt | @@ -7,7 +7,7 @@ import pylab as plt | ||
7 | import mjpeg | 7 | import mjpeg |
8 | import mjsteg | 8 | import mjsteg |
9 | import jpegObj | 9 | import jpegObj |
10 | -from msteg.steganography import F3, F4, LSB | 10 | +from msteg.steganography import LSB, F3, F4, F5 |
11 | from common import * | 11 | from common import * |
12 | 12 | ||
13 | 13 | ||
@@ -22,13 +22,17 @@ sample = [[7, 12, 14, -12, 1, 0, -1, 0], | @@ -22,13 +22,17 @@ sample = [[7, 12, 14, -12, 1, 0, -1, 0], | ||
22 | [0, 0, 0, 0, 0, 0, 0, 0], | 22 | [0, 0, 0, 0, 0, 0, 0, 0], |
23 | [0, 0, 0, 0, 0, 0, 0, 0]] | 23 | [0, 0, 0, 0, 0, 0, 0, 0]] |
24 | 24 | ||
25 | -sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, 5908L, 59816L, 56765L] | 25 | +sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, |
26 | + 5908L, 59816L, 56765L] | ||
26 | 27 | ||
27 | txtsample = [116, 104, 105, 115, 32, 105, 115, 32, 116, 111, 32, 98, 101, 32, 101, 109, 98, 101, 100, 101, 100, 46, 10] | 28 | txtsample = [116, 104, 105, 115, 32, 105, 115, 32, 116, 111, 32, 98, 101, 32, 101, 109, 98, 101, 100, 101, 100, 46, 10] |
28 | 29 | ||
29 | if __name__ == '__main__': | 30 | if __name__ == '__main__': |
30 | - f3test = F4.F4() | 31 | + # f3test = F4.F4(sample_key) |
32 | + f3test = F5.F5(sample_key, 3) | ||
31 | f3test.embed_raw_data("res/test3.jpg", "res/embeded", "res/steged.jpg") | 33 | f3test.embed_raw_data("res/test3.jpg", "res/embeded", "res/steged.jpg") |
34 | + | ||
35 | + # f3test2 = F4.F4(sample_key) | ||
32 | f3test.extract_raw_data("res/steged.jpg", "res/extracted") | 36 | f3test.extract_raw_data("res/steged.jpg", "res/extracted") |
33 | print f3test.get_key() | 37 | print f3test.get_key() |
34 | pass | 38 | pass |