zindosteg 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +92 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/zindosteg/aes.cpp +73 -0
- data/ext/zindosteg/aes.h +36 -0
- data/ext/zindosteg/bmp.cpp +100 -0
- data/ext/zindosteg/bmp.h +39 -0
- data/ext/zindosteg/device.cpp +244 -0
- data/ext/zindosteg/device.h +69 -0
- data/ext/zindosteg/extconf.rb +10 -0
- data/ext/zindosteg/file_utils.h +36 -0
- data/ext/zindosteg/hmac.h +82 -0
- data/ext/zindosteg/jpeg.cpp +111 -0
- data/ext/zindosteg/jpeg.h +39 -0
- data/ext/zindosteg/jpeg_helpers.cpp +149 -0
- data/ext/zindosteg/jpeg_helpers.h +54 -0
- data/ext/zindosteg/key_generator.cpp +36 -0
- data/ext/zindosteg/key_generator.h +23 -0
- data/ext/zindosteg/loader.cpp +72 -0
- data/ext/zindosteg/permutator.cpp +129 -0
- data/ext/zindosteg/permutator.h +39 -0
- data/ext/zindosteg/png_provider.cpp +304 -0
- data/ext/zindosteg/png_provider.h +87 -0
- data/ext/zindosteg/provider.h +40 -0
- data/ext/zindosteg/steg_defs.h +24 -0
- data/ext/zindosteg/steg_endian.h +168 -0
- data/ext/zindosteg/zindosteg.cpp +704 -0
- data/lib/zindosteg/version.rb +3 -0
- data/lib/zindosteg.rb +18 -0
- data/zindosteg.gemspec +28 -0
- metadata +108 -0
@@ -0,0 +1,129 @@
|
|
1
|
+
#include "./permutator.h"
|
2
|
+
#include <openssl/evp.h>
|
3
|
+
#include "steg_endian.h"
|
4
|
+
#include <algorithm>
|
5
|
+
|
6
|
+
namespace zindorsky {
|
7
|
+
namespace permutator {
|
8
|
+
|
9
|
+
namespace {
|
10
|
+
struct key_cstr_helper {
|
11
|
+
key_cstr_helper(crypto::key_generator const& generator, size_t len)
|
12
|
+
: key(len)
|
13
|
+
{
|
14
|
+
generator.generate(key.data(), key.size());
|
15
|
+
}
|
16
|
+
|
17
|
+
byte_vector key;
|
18
|
+
};
|
19
|
+
} //namespace
|
20
|
+
|
21
|
+
context::context(index_t array_size, crypto::key_generator const& generator, int keylen)
|
22
|
+
: context{array_size, key_cstr_helper{generator, static_cast<size_t>(keylen)}.key.data(), keylen}
|
23
|
+
{
|
24
|
+
}
|
25
|
+
|
26
|
+
context::context(index_t array_size, byte const* key, int keylen)
|
27
|
+
: context{array_size, crypto::aes{key, keylen}}
|
28
|
+
{
|
29
|
+
}
|
30
|
+
|
31
|
+
context::context(index_t array_size, crypto::aes const& key)
|
32
|
+
: size_{array_size}
|
33
|
+
, key_{key}
|
34
|
+
{
|
35
|
+
for(bitlen_=0; array_size!=0; ++bitlen_)
|
36
|
+
array_size >>= 1;
|
37
|
+
split_ = bitlen_/2;
|
38
|
+
split_mask_[0] = (1<<split_)-1;
|
39
|
+
split_mask_[1] = (1<<((bitlen_+1)/2))-1;
|
40
|
+
|
41
|
+
if( bitlen_ <= 9 ) {
|
42
|
+
rounds_ = 36;
|
43
|
+
} else if( bitlen_ <= 13 ) {
|
44
|
+
rounds_ = 30;
|
45
|
+
} else if( bitlen_ <= 19 ) {
|
46
|
+
rounds_ = 24;
|
47
|
+
} else if( bitlen_ <= 31 ) {
|
48
|
+
rounds_ = 18;
|
49
|
+
} else {
|
50
|
+
rounds_ = 12;
|
51
|
+
}
|
52
|
+
|
53
|
+
//Pre-compute AES(P)
|
54
|
+
//VERS
|
55
|
+
P_templ_[0] = 0; P_templ_[1]=1;
|
56
|
+
//method
|
57
|
+
P_templ_[2] = 2;
|
58
|
+
//addition
|
59
|
+
P_templ_[3] = 0;
|
60
|
+
//radix
|
61
|
+
P_templ_[4] = 2;
|
62
|
+
//n
|
63
|
+
P_templ_[5] = bitlen_;
|
64
|
+
//split(n)
|
65
|
+
P_templ_[6] = split_;
|
66
|
+
//rnds(n)
|
67
|
+
P_templ_[7] = rounds_;
|
68
|
+
//tweak length (no tweak in this implementation)
|
69
|
+
std::fill_n(&P_templ_[8], 8, 0);
|
70
|
+
key_.encrypt(P_templ_,P_templ_);
|
71
|
+
}
|
72
|
+
|
73
|
+
//AES-FFX-A2 encrypt
|
74
|
+
index_t context::operator[] (index_t index) const
|
75
|
+
{
|
76
|
+
half_t A = static_cast<half_t>( index & split_mask_[0] );
|
77
|
+
half_t B = static_cast<half_t>( index >> split_ );
|
78
|
+
|
79
|
+
for(byte i=0; i<rounds_; ++i) {
|
80
|
+
half_t C = A ^ F(i,B);
|
81
|
+
A = B;
|
82
|
+
B = C;
|
83
|
+
}
|
84
|
+
|
85
|
+
index_t retval = (static_cast<index_t>(B)<<split_) | static_cast<index_t>(A);
|
86
|
+
|
87
|
+
//Chain-walking:
|
88
|
+
if( retval >= size_ ) {
|
89
|
+
return operator[](retval);
|
90
|
+
}
|
91
|
+
return retval;
|
92
|
+
}
|
93
|
+
|
94
|
+
//AES-FFX-A2 decrypt
|
95
|
+
index_t context::reverse(index_t index) const
|
96
|
+
{
|
97
|
+
half_t A = static_cast<half_t>( index & split_mask_[0] );
|
98
|
+
half_t B = static_cast<half_t>( index >> split_ );
|
99
|
+
|
100
|
+
for(byte i=rounds_; i>0; --i) {
|
101
|
+
half_t C = B;
|
102
|
+
B = A;
|
103
|
+
A = C ^ F(i-1,B);
|
104
|
+
}
|
105
|
+
|
106
|
+
index_t retval = (static_cast<index_t>(B)<<split_) | static_cast<index_t>(A);
|
107
|
+
|
108
|
+
//Chain-walking:
|
109
|
+
if( retval >= size_ ) {
|
110
|
+
return reverse(retval);
|
111
|
+
}
|
112
|
+
return retval;
|
113
|
+
}
|
114
|
+
|
115
|
+
context::half_t context::F(byte r, half_t B) const
|
116
|
+
{
|
117
|
+
byte Q[AES_BLOCK_SIZE] = {0};
|
118
|
+
//Fill out Q. (No tweak in this implementation.)
|
119
|
+
Q[7] = r;
|
120
|
+
endian::write_be(B,&Q[sizeof(Q)-sizeof(B)]);
|
121
|
+
for(std::size_t i=0; i<sizeof(Q); ++i) {
|
122
|
+
Q[i] ^= P_templ_[i];
|
123
|
+
}
|
124
|
+
key_.encrypt(Q,Q);
|
125
|
+
endian::read_be(&Q[sizeof(Q)-sizeof(B)],B);
|
126
|
+
return B & split_mask_[r%2];
|
127
|
+
}
|
128
|
+
|
129
|
+
}} //namespace zindorsky::permutator
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
/* An implementation of the AES-FFX-A2 algorithm as a means to a low-memory, random-access, cryptographically-strong, index permutator.
|
4
|
+
See http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ffx/ffx-spec.pdf for the spec.
|
5
|
+
*/
|
6
|
+
|
7
|
+
#include "steg_defs.h"
|
8
|
+
#include <cstring>
|
9
|
+
#include "aes.h"
|
10
|
+
#include "key_generator.h"
|
11
|
+
#include <cstdint>
|
12
|
+
|
13
|
+
namespace zindorsky {
|
14
|
+
namespace permutator {
|
15
|
+
|
16
|
+
using index_t = uint_fast64_t;
|
17
|
+
|
18
|
+
class context {
|
19
|
+
public:
|
20
|
+
context(index_t array_size, crypto::key_generator const& generator, int keylen = 16);
|
21
|
+
context(index_t array_size, byte const* key, int keylen = 16);
|
22
|
+
context(index_t array_size, crypto::aes const& key);
|
23
|
+
|
24
|
+
index_t operator[] (index_t index) const;
|
25
|
+
index_t reverse (index_t index) const;
|
26
|
+
|
27
|
+
private:
|
28
|
+
using half_t = uint_fast32_t;
|
29
|
+
|
30
|
+
index_t size_;
|
31
|
+
crypto::aes key_;
|
32
|
+
byte bitlen_, split_, rounds_;
|
33
|
+
byte P_templ_[AES_BLOCK_SIZE];
|
34
|
+
half_t split_mask_[2];
|
35
|
+
|
36
|
+
half_t F(byte r, half_t B) const;
|
37
|
+
};
|
38
|
+
|
39
|
+
}} //namespace zindorsky::permutator
|
@@ -0,0 +1,304 @@
|
|
1
|
+
#include "png_provider.h"
|
2
|
+
#include <exception>
|
3
|
+
#include "file_utils.h"
|
4
|
+
#include "string.h"
|
5
|
+
|
6
|
+
namespace zindorsky {
|
7
|
+
namespace steganography {
|
8
|
+
|
9
|
+
namespace {
|
10
|
+
|
11
|
+
struct read_context {
|
12
|
+
read_context(byte const* data, size_t size) : data{data}, size{size} {}
|
13
|
+
byte const* data;
|
14
|
+
size_t size;
|
15
|
+
size_t pos = 0;
|
16
|
+
};
|
17
|
+
|
18
|
+
void read_data(png_structp png_ptr, png_bytep data, png_size_t length)
|
19
|
+
{
|
20
|
+
auto source = reinterpret_cast<read_context*>(png_get_io_ptr(png_ptr));
|
21
|
+
if (source->pos + length >= source->size) {
|
22
|
+
length = source->size - source->pos;
|
23
|
+
}
|
24
|
+
memcpy(data, source->data + source->pos, length);
|
25
|
+
source->pos += length;
|
26
|
+
}
|
27
|
+
|
28
|
+
void write_data(png_structp png_ptr, png_bytep data, png_size_t length)
|
29
|
+
{
|
30
|
+
byte_vector* sink = reinterpret_cast<byte_vector*>(png_get_io_ptr(png_ptr));
|
31
|
+
sink->insert(sink->end(), data, data + length);
|
32
|
+
}
|
33
|
+
|
34
|
+
void flush_data(png_structp)
|
35
|
+
{
|
36
|
+
}
|
37
|
+
|
38
|
+
} //namespace
|
39
|
+
|
40
|
+
const byte png_provider::signature[8] = {0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A};
|
41
|
+
|
42
|
+
png_provider::png_provider(filesystem::path const& filename)
|
43
|
+
: png_provider( utils::load_from_file(filename) )
|
44
|
+
{
|
45
|
+
}
|
46
|
+
|
47
|
+
png_provider::png_provider(byte_vector const& data)
|
48
|
+
: png_provider(data.data(), data.size())
|
49
|
+
{
|
50
|
+
}
|
51
|
+
|
52
|
+
png_provider::png_provider(byte const* data, size_t size)
|
53
|
+
{
|
54
|
+
if(size < sizeof(signature) || memcmp(data, signature, sizeof(signature))!=0) {
|
55
|
+
throw invalid_carrier();
|
56
|
+
}
|
57
|
+
|
58
|
+
ctx_ = std::make_unique<png_read_ctx>();
|
59
|
+
if (setjmp(png_jmpbuf(ctx_->ptr))) {
|
60
|
+
throw invalid_carrier();
|
61
|
+
}
|
62
|
+
|
63
|
+
read_context read_ctx{data, size};
|
64
|
+
png_set_read_fn(ctx_->ptr, &read_ctx, read_data);
|
65
|
+
|
66
|
+
png_read_info(ctx_->ptr, ctx_->info);
|
67
|
+
|
68
|
+
width_ = png_get_image_width(ctx_->ptr, ctx_->info);
|
69
|
+
height_ = png_get_image_height(ctx_->ptr, ctx_->info);
|
70
|
+
bit_depth_ = png_get_bit_depth(ctx_->ptr, ctx_->info);
|
71
|
+
color_type_ = png_get_color_type(ctx_->ptr, ctx_->info);
|
72
|
+
|
73
|
+
if( (color_type_==0 && bit_depth_!=1 && bit_depth_!=2 && bit_depth_!=4 && bit_depth_!=8 && bit_depth_!=16)
|
74
|
+
|| ((color_type_==2 || color_type_==4 || color_type_==6) && bit_depth_!=8 && bit_depth_!=16)
|
75
|
+
|| (color_type_==3 &&bit_depth_!=1 && bit_depth_!=2 && bit_depth_!=4 && bit_depth_!=8)
|
76
|
+
) {
|
77
|
+
throw invalid_carrier();
|
78
|
+
}
|
79
|
+
|
80
|
+
//We're going to disallow images with bit depth less than 8, since hidden data is more noticeable in them.
|
81
|
+
if(bit_depth_ < 8) {
|
82
|
+
throw invalid_carrier("PNG bit depth too small");
|
83
|
+
}
|
84
|
+
//Palette types are also not good for steganography:
|
85
|
+
if(color_type_ & 1) {
|
86
|
+
throw invalid_carrier("palette using PNG files not supported");
|
87
|
+
}
|
88
|
+
|
89
|
+
auto row_size = png_get_rowbytes(ctx_->ptr, ctx_->info);
|
90
|
+
data_.resize(row_size * height_);
|
91
|
+
row_pointers_.resize(height_);
|
92
|
+
for(auto i = 0U; i < height_; ++i) {
|
93
|
+
row_pointers_[i] = &data_[0] + i * row_size;
|
94
|
+
}
|
95
|
+
png_read_image(ctx_->ptr, &row_pointers_[0]);
|
96
|
+
}
|
97
|
+
|
98
|
+
provider_t::index_t png_provider::size() const
|
99
|
+
{
|
100
|
+
return data_.size() / (bit_depth_ / 8);
|
101
|
+
}
|
102
|
+
|
103
|
+
byte & png_provider::access_indexed_data(provider_t::index_t index)
|
104
|
+
{
|
105
|
+
return data_[adjust_index(index)];
|
106
|
+
}
|
107
|
+
|
108
|
+
byte const& png_provider::access_indexed_data(index_t index) const
|
109
|
+
{
|
110
|
+
return data_[adjust_index(index)];
|
111
|
+
}
|
112
|
+
|
113
|
+
byte_vector png_provider::commit_to_memory()
|
114
|
+
{
|
115
|
+
byte_vector data;
|
116
|
+
png_write_ctx write_ctx;
|
117
|
+
if (setjmp(png_jmpbuf(write_ctx.ptr))) {
|
118
|
+
throw std::exception();
|
119
|
+
}
|
120
|
+
png_set_write_fn(write_ctx.ptr, &data, write_data, flush_data);
|
121
|
+
write_ctx.copy_from_read(*ctx_);
|
122
|
+
|
123
|
+
png_write_info(write_ctx.ptr, write_ctx.info);
|
124
|
+
png_write_image(write_ctx.ptr, &row_pointers_[0]);
|
125
|
+
png_write_end(write_ctx.ptr, nullptr);
|
126
|
+
|
127
|
+
return data;
|
128
|
+
}
|
129
|
+
|
130
|
+
void png_provider::commit_to_file(filesystem::path const& file)
|
131
|
+
{
|
132
|
+
FILE *f = fopen(file.string().c_str(), "wb");
|
133
|
+
png_write_ctx write_ctx;
|
134
|
+
if (setjmp(png_jmpbuf(write_ctx.ptr))) {
|
135
|
+
if(f) fclose(f);
|
136
|
+
throw std::exception();
|
137
|
+
}
|
138
|
+
png_init_io(write_ctx.ptr, f);
|
139
|
+
write_ctx.copy_from_read(*ctx_);
|
140
|
+
|
141
|
+
png_write_info(write_ctx.ptr, write_ctx.info);
|
142
|
+
png_write_image(write_ctx.ptr, &row_pointers_[0]);
|
143
|
+
png_write_end(write_ctx.ptr, nullptr);
|
144
|
+
fclose(f);
|
145
|
+
}
|
146
|
+
|
147
|
+
byte_vector png_provider::salt() const
|
148
|
+
{
|
149
|
+
byte salt[8]={0};
|
150
|
+
for(std::size_t i=0; i<height_; ++i) {
|
151
|
+
salt[ i%sizeof(salt) ] += access_indexed_data(i*width_ + i%width_)>>1;
|
152
|
+
}
|
153
|
+
|
154
|
+
return byte_vector(salt,salt+sizeof(salt));
|
155
|
+
}
|
156
|
+
|
157
|
+
size_t png_provider::adjust_index(size_t index) const
|
158
|
+
{
|
159
|
+
return index * (bit_depth_ / 8);
|
160
|
+
}
|
161
|
+
|
162
|
+
|
163
|
+
void png_write_ctx::copy_from_read(png_read_ctx const& read_ctx)
|
164
|
+
{
|
165
|
+
std::uint32_t width, height;
|
166
|
+
int bit_depth, color_type;
|
167
|
+
int interlace_type, compression_type, filter_type;
|
168
|
+
|
169
|
+
if (png_get_IHDR(read_ctx.ptr, read_ctx.info, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type))
|
170
|
+
png_set_IHDR(ptr, info, width, height, bit_depth,
|
171
|
+
#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
|
172
|
+
color_type, interlace_type, compression_type, filter_type
|
173
|
+
#else
|
174
|
+
color_type, PNG_INTERLACE_NONE, compression_type, filter_type
|
175
|
+
#endif
|
176
|
+
);
|
177
|
+
#if defined(PNG_FIXED_POINT_SUPPORTED)
|
178
|
+
#if defined(PNG_cHRM_SUPPORTED)
|
179
|
+
png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y;
|
180
|
+
if (png_get_cHRM_fixed(read_ctx.ptr, read_ctx.info, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y))
|
181
|
+
png_set_cHRM_fixed(ptr, info, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y);
|
182
|
+
#endif
|
183
|
+
#if defined(PNG_gAMA_SUPPORTED)
|
184
|
+
png_fixed_point gamma;
|
185
|
+
if (png_get_gAMA_fixed(read_ctx.ptr, read_ctx.info, &gamma))
|
186
|
+
png_set_gAMA_fixed(ptr, info, gamma);
|
187
|
+
#endif
|
188
|
+
#else /* Use floating point versions */
|
189
|
+
#if defined(PNG_FLOATING_POINT_SUPPORTED)
|
190
|
+
#if defined(PNG_cHRM_SUPPORTED)
|
191
|
+
double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y;
|
192
|
+
if (png_get_cHRM(read_ctx.ptr, read_ctx.info, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y))
|
193
|
+
png_set_cHRM(ptr, info, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y);
|
194
|
+
#endif
|
195
|
+
#if defined(PNG_gAMA_SUPPORTED)
|
196
|
+
double gamma;
|
197
|
+
if (png_get_gAMA(read_ctx.ptr, read_ctx.info, &gamma))
|
198
|
+
png_set_gAMA(ptr, info, gamma);
|
199
|
+
#endif
|
200
|
+
#endif /* floating point */
|
201
|
+
#endif /* fixed point */
|
202
|
+
#if defined(PNG_iCCP_SUPPORTED)
|
203
|
+
png_charp name;
|
204
|
+
png_bytep profile;
|
205
|
+
png_uint_32 proflen;
|
206
|
+
if (png_get_iCCP(read_ctx.ptr, read_ctx.info, &name, &compression_type, &profile, &proflen))
|
207
|
+
png_set_iCCP(ptr, info, name, compression_type, profile, proflen);
|
208
|
+
#endif
|
209
|
+
#if defined(PNG_sRGB_SUPPORTED)
|
210
|
+
int intent;
|
211
|
+
if (png_get_sRGB(read_ctx.ptr, read_ctx.info, &intent))
|
212
|
+
png_set_sRGB(ptr, info, intent);
|
213
|
+
#endif
|
214
|
+
png_colorp palette;
|
215
|
+
int num_palette;
|
216
|
+
if (png_get_PLTE(read_ctx.ptr, read_ctx.info, &palette, &num_palette))
|
217
|
+
png_set_PLTE(ptr, info, palette, num_palette);
|
218
|
+
#if defined(PNG_bKGD_SUPPORTED)
|
219
|
+
png_color_16p background;
|
220
|
+
if (png_get_bKGD(read_ctx.ptr, read_ctx.info, &background))
|
221
|
+
png_set_bKGD(ptr, info, background);
|
222
|
+
#endif
|
223
|
+
#if defined(PNG_hIST_SUPPORTED)
|
224
|
+
png_uint_16p hist;
|
225
|
+
if (png_get_hIST(read_ctx.ptr, read_ctx.info, &hist))
|
226
|
+
png_set_hIST(ptr, info, hist);
|
227
|
+
#endif
|
228
|
+
#if defined(PNG_oFFs_SUPPORTED)
|
229
|
+
png_int_32 offset_x, offset_y;
|
230
|
+
int unit_type;
|
231
|
+
if (png_get_oFFs(read_ctx.ptr, read_ctx.info,&offset_x,&offset_y,&unit_type))
|
232
|
+
png_set_oFFs(ptr, info, offset_x, offset_y, unit_type);
|
233
|
+
#endif
|
234
|
+
#if defined(PNG_pCAL_SUPPORTED)
|
235
|
+
png_charp purpose, units;
|
236
|
+
png_charpp params;
|
237
|
+
png_int_32 X0, X1;
|
238
|
+
int type, nparams;
|
239
|
+
|
240
|
+
if (png_get_pCAL(read_ctx.ptr, read_ctx.info, &purpose, &X0, &X1, &type, &nparams, &units, ¶ms))
|
241
|
+
png_set_pCAL(ptr, info, purpose, X0, X1, type, nparams, units, params);
|
242
|
+
#endif
|
243
|
+
#if defined(PNG_pHYs_SUPPORTED)
|
244
|
+
png_uint_32 res_x, res_y;
|
245
|
+
if (png_get_pHYs(read_ctx.ptr, read_ctx.info, &res_x, &res_y, &unit_type))
|
246
|
+
png_set_pHYs(ptr, info, res_x, res_y, unit_type);
|
247
|
+
#endif
|
248
|
+
#if defined(PNG_sBIT_SUPPORTED)
|
249
|
+
png_color_8p sig_bit;
|
250
|
+
|
251
|
+
if (png_get_sBIT(read_ctx.ptr, read_ctx.info, &sig_bit))
|
252
|
+
png_set_sBIT(ptr, info, sig_bit);
|
253
|
+
#endif
|
254
|
+
#if defined(PNG_sCAL_SUPPORTED)
|
255
|
+
#ifdef PNG_FLOATING_POINT_SUPPORTED
|
256
|
+
int unit;
|
257
|
+
double scal_width, scal_height;
|
258
|
+
|
259
|
+
if (png_get_sCAL(read_ctx.ptr, read_ctx.info, &unit, &scal_width, &scal_height))
|
260
|
+
png_set_sCAL(ptr, info, unit, scal_width, scal_height);
|
261
|
+
#else
|
262
|
+
#ifdef PNG_FIXED_POINT_SUPPORTED
|
263
|
+
int unit;
|
264
|
+
png_charp scal_width, scal_height;
|
265
|
+
|
266
|
+
if (png_get_sCAL_s(read_ctx.ptr, read_ctx.info, &unit, &scal_width, &scal_height))
|
267
|
+
png_set_sCAL_s(ptr, info, unit, scal_width, scal_height);
|
268
|
+
#endif
|
269
|
+
#endif
|
270
|
+
#endif
|
271
|
+
#if defined(PNG_TEXT_SUPPORTED)
|
272
|
+
png_textp text_ptr;
|
273
|
+
int num_text;
|
274
|
+
if (png_get_text(read_ctx.ptr, read_ctx.info, &text_ptr, &num_text) > 0)
|
275
|
+
png_set_text(ptr, info, text_ptr, num_text);
|
276
|
+
#endif
|
277
|
+
#if defined(PNG_tIME_SUPPORTED)
|
278
|
+
png_timep mod_time;
|
279
|
+
|
280
|
+
if (png_get_tIME(read_ctx.ptr, read_ctx.info, &mod_time)) {
|
281
|
+
png_set_tIME(ptr, info, mod_time);
|
282
|
+
}
|
283
|
+
#endif
|
284
|
+
#if defined(PNG_tRNS_SUPPORTED)
|
285
|
+
png_bytep trans;
|
286
|
+
int num_trans;
|
287
|
+
png_color_16p trans_values;
|
288
|
+
if (png_get_tRNS(read_ctx.ptr, read_ctx.info, &trans, &num_trans, &trans_values))
|
289
|
+
png_set_tRNS(ptr, info, trans, num_trans, trans_values);
|
290
|
+
#endif
|
291
|
+
#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
|
292
|
+
png_unknown_chunkp unknowns;
|
293
|
+
int num_unknowns = (int)png_get_unknown_chunks(read_ctx.ptr, read_ctx.info, &unknowns);
|
294
|
+
if (num_unknowns) {
|
295
|
+
png_size_t i;
|
296
|
+
png_set_unknown_chunks(ptr, info, unknowns, num_unknowns);
|
297
|
+
for (i = 0; i < num_unknowns; i++)
|
298
|
+
png_set_unknown_chunk_location(ptr, info, i, unknowns[i].location);
|
299
|
+
}
|
300
|
+
#endif
|
301
|
+
}
|
302
|
+
|
303
|
+
}} //namespace
|
304
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "provider.h"
|
4
|
+
#include <vector>
|
5
|
+
#include <cstdint>
|
6
|
+
#include <png.h>
|
7
|
+
#include <memory>
|
8
|
+
|
9
|
+
namespace zindorsky {
|
10
|
+
namespace steganography {
|
11
|
+
|
12
|
+
struct png_read_ctx {
|
13
|
+
png_structp ptr = nullptr;
|
14
|
+
png_infop info = nullptr;
|
15
|
+
|
16
|
+
png_read_ctx()
|
17
|
+
{
|
18
|
+
ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
19
|
+
if (ptr) {
|
20
|
+
info = png_create_info_struct(ptr);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
~png_read_ctx()
|
25
|
+
{
|
26
|
+
if(ptr) {
|
27
|
+
png_destroy_read_struct(&ptr, info ? &info : nullptr, nullptr);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
};
|
31
|
+
|
32
|
+
struct png_write_ctx {
|
33
|
+
png_structp ptr = nullptr;
|
34
|
+
png_infop info = nullptr;
|
35
|
+
|
36
|
+
png_write_ctx()
|
37
|
+
{
|
38
|
+
ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
39
|
+
if (ptr) {
|
40
|
+
info = png_create_info_struct(ptr);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
~png_write_ctx()
|
45
|
+
{
|
46
|
+
if(ptr) {
|
47
|
+
png_destroy_write_struct(&ptr, info ? &info : nullptr);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
void copy_from_read(png_read_ctx const& read_ctx);
|
52
|
+
};
|
53
|
+
|
54
|
+
class png_provider : public provider_t {
|
55
|
+
public:
|
56
|
+
explicit png_provider(filesystem::path const& filename);
|
57
|
+
explicit png_provider(byte_vector const& data);
|
58
|
+
png_provider(byte const* data, size_t size);
|
59
|
+
|
60
|
+
//Non-copyable:
|
61
|
+
png_provider(png_provider const&) = delete;
|
62
|
+
png_provider & operator = (png_provider const&) = delete;
|
63
|
+
//Movable:
|
64
|
+
png_provider(png_provider &&) = default;
|
65
|
+
png_provider & operator = (png_provider &&) = default;
|
66
|
+
|
67
|
+
static std::string format() { return "PNG"; }
|
68
|
+
virtual index_t size() const override;
|
69
|
+
virtual byte & access_indexed_data(index_t index) override;
|
70
|
+
virtual byte const& access_indexed_data(index_t index) const override;
|
71
|
+
virtual byte_vector commit_to_memory() override;
|
72
|
+
virtual void commit_to_file(filesystem::path const& file) override;
|
73
|
+
virtual byte_vector salt() const override;
|
74
|
+
|
75
|
+
static const byte signature[8];
|
76
|
+
|
77
|
+
private:
|
78
|
+
std::unique_ptr<png_read_ctx> ctx_;
|
79
|
+
byte_vector data_;
|
80
|
+
std::vector<byte*> row_pointers_;
|
81
|
+
std::uint32_t width_, height_;
|
82
|
+
byte bit_depth_, color_type_;
|
83
|
+
|
84
|
+
size_t adjust_index(size_t index) const;
|
85
|
+
};
|
86
|
+
|
87
|
+
}} //namespace zindorsky::steganography
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "steg_defs.h"
|
4
|
+
#include <vector>
|
5
|
+
#include <memory>
|
6
|
+
#include <cstdint>
|
7
|
+
#include <exception>
|
8
|
+
|
9
|
+
namespace zindorsky {
|
10
|
+
namespace steganography {
|
11
|
+
|
12
|
+
class invalid_carrier : public std::runtime_error {
|
13
|
+
public:
|
14
|
+
invalid_carrier() : std::runtime_error{"Invalid carrier file"} {}
|
15
|
+
explicit invalid_carrier(char const* msg) : std::runtime_error{msg} {}
|
16
|
+
};
|
17
|
+
|
18
|
+
class provider_t {
|
19
|
+
public:
|
20
|
+
//Loads from file.
|
21
|
+
static std::unique_ptr<provider_t> load(filesystem::path const& file);
|
22
|
+
//Loads from memory. Caller retains ownership of buffer.
|
23
|
+
static std::unique_ptr<provider_t> load(void const* data, size_t size);
|
24
|
+
|
25
|
+
static std::vector<std::string> supported_formats();
|
26
|
+
|
27
|
+
using index_t = uint_fast64_t;
|
28
|
+
|
29
|
+
virtual ~provider_t() {}
|
30
|
+
|
31
|
+
virtual index_t size() const = 0;
|
32
|
+
virtual byte & access_indexed_data(index_t index) = 0;
|
33
|
+
virtual byte const& access_indexed_data( index_t index ) const = 0;
|
34
|
+
virtual byte_vector commit_to_memory() = 0;
|
35
|
+
virtual void commit_to_file(filesystem::path const& file) = 0;
|
36
|
+
virtual byte_vector salt() const = 0;
|
37
|
+
|
38
|
+
};
|
39
|
+
|
40
|
+
}} //namespace zindorsky::steganography
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <vector>
|
4
|
+
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 9
|
5
|
+
# define EXPERIMENTAL_FILESYSTEM
|
6
|
+
# include <experimental/filesystem>
|
7
|
+
#else
|
8
|
+
# include <filesystem>
|
9
|
+
#endif
|
10
|
+
|
11
|
+
namespace zindorsky {
|
12
|
+
|
13
|
+
using byte = unsigned char;
|
14
|
+
using byte_vector = std::vector<byte>;
|
15
|
+
|
16
|
+
#if defined(EXPERIMENTAL_FILESYSTEM)
|
17
|
+
# include <experimental/filesystem>
|
18
|
+
namespace filesystem = std::experimental::filesystem;
|
19
|
+
#else
|
20
|
+
# include <filesystem>
|
21
|
+
namespace filesystem = std::filesystem;
|
22
|
+
#endif
|
23
|
+
|
24
|
+
} // namespace zindorsky
|