zindosteg 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 01ac48210a386fca7fec50d18a609ced3f2122f0087f1fc6ead5a14970e89968
4
+ data.tar.gz: 4d11432c88bb155006a58f9f9dda92d292afb7dd5a4b671cf086b5a54467cc69
5
+ SHA512:
6
+ metadata.gz: '06884588dc026719a0e8f86774075432e18a459f7aa586677499bfcce31d5664705e1d06eeab09f52ddf4f8126a50e15fc26acda500c852309d7532ae87285d9'
7
+ data.tar.gz: c860ce6d6f4c160fcf6d5b02066b661777fd41abde8189b5affc9a0b8eef3785e8a608f1a09079001ce3d94d2bb20d346ab92557d7836ca25cd3d66003b4ba24
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.5.1
6
+ before_install: gem install bundler -v 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in zindosteg.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Nephi Allred
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # Zindosteg
2
+
3
+ Steganography is the art and science of hiding data inside another set of data. The hidden data is known as the "payload", and could be any kind of data. The data that holds the hidden data is called the "carrier" and is normally an image, sound, or video file. The goal is to modify the carrier file in such a way that to human eyes it appears the same as before, but the subtle differences actually encode bits (0s and 1s) that can be reassembled into the payload.
4
+
5
+ This gem is a Ruby interface to the Zindosteg C++ library, which uses a variant of the F5 data hiding technique.
6
+
7
+ ## Features
8
+ * Encryption: The payload is encrypted with AES-256-CTR using a key derived from the password using the carrier file as salt.
9
+ * Data integrity: In order to protect against accidental corruption or intentional tampering, the payload is stored with an HMAC.
10
+ * Scattering: Instead of placing the payload sequentially inside the carrier file, the order of the hidden bits is determined by a pseudo-random number generator seeded with the password.
11
+
12
+ ## Supported Carrier Types
13
+ Zindosteg supports JPEG, PNG, and BMP carrier files. PNG files must have at least 8 bit depth, and not be "palette" type. BMP files must be 24-bit.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'zindosteg'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle install
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install zindosteg
30
+
31
+ Note: To compile the native extensions, you may need to install JPEG, PNG, and OpenSSL development packages.
32
+
33
+ ## Usage
34
+ Zindosteg is designed to mimic Ruby's `File` class as closely as possible. The basic idea is that you get a `Zindosteg` instance by "opening" the carrier file with a password. Then you can read and write to the `Zindosteg` object in the same way you normally would to a `File` object, using the same methods (e.g. `read`, `write`, `readlines`, `eof?`, `seek`, `tell`, etc.). You can even pass the `Zindosteg` instance to functions that expect `File` objects and everything should work.
35
+
36
+ ### Examples
37
+ ```
38
+ # Open a new carrier for writing (overwriting any existing payload)
39
+ file = ::Zindosteg::File.open("carrier.jpeg", "secretpassword", "w")
40
+
41
+ # Write the payload to it
42
+ file.write("This is my secret payload.")
43
+
44
+ # Close to finalize
45
+ file.close
46
+
47
+ # Open an existing carrier file for reading
48
+ file = ::Zindosteg::File.open("carrier.jpeg", "secretpassword", "r")
49
+
50
+ # The mode parameter is "r" by default so this is equivalent to the above:
51
+ file = ::Zindosteg::File.open("carrier.jpeg", "secretpassword")
52
+
53
+ # Read the payload
54
+ file.read
55
+
56
+ # Or you could read it as an array of lines.
57
+ file.readlines
58
+
59
+ # Use all the regular File methods as you would normally
60
+ file.seek(0)
61
+ file.tell
62
+ file.size
63
+ file.eof?
64
+ # etc
65
+
66
+ # Use the `capacity` method to see the maximum number of bytes that the carrier file can hide.
67
+ file.capacity
68
+
69
+ # All the standard modes for opening files are supported:
70
+ file = ::Zindosteg::File.open("carrier.jpeg", "secretpassword", "w+") # Opens for reading and writing, truncating any existing payload
71
+
72
+ file = ::Zindosteg::File.open("carrier.jpeg", "secretpassword", "a") # Opens for appending
73
+
74
+ # etc
75
+
76
+ # If you open for reading (or appending) and either (1) an incorrect password is given, or (2) there is no existing payload, or (3) the carrier file has been corrupted or tampered with, then a exception will be thrown.
77
+
78
+ ```
79
+
80
+ ## Development
81
+
82
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
83
+
84
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
85
+
86
+ ## Contributing
87
+
88
+ Bug reports and pull requests are welcome on GitHub at https://github.com/zindorsky/zindosteg-ruby.
89
+
90
+ ## License
91
+
92
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "rake/extensiontask"
4
+
5
+ Rake::ExtensionTask.new "zindosteg" do |ext|
6
+ ext.lib_dir = "lib/zindosteg"
7
+ end
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "zindosteg"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,73 @@
1
+ #include "./aes.h"
2
+ #include <string.h>
3
+ #include <algorithm>
4
+ #include "steg_endian.h"
5
+
6
+ namespace zindorsky {
7
+ namespace crypto {
8
+
9
+ aes::aes(byte const* key, int keysize) { rekey(key,keysize); }
10
+ void aes::encrypt(byte const* in, byte * out) const { AES_encrypt(in,out,&ekey_); }
11
+ void aes::decrypt(byte const* in, byte * out) const { AES_decrypt(in,out,&dkey_); }
12
+ void aes::rekey(byte const* key, int keysize) { AES_set_encrypt_key(key,keysize*8,&ekey_); AES_set_decrypt_key(key,keysize*8,&dkey_); }
13
+
14
+ namespace {
15
+
16
+ void add128(byte * a, int p)
17
+ {
18
+ std::int64_t a0,a1;
19
+ endian::read_be(a+8,a0);
20
+ a1 = a0+p;
21
+ endian::write_be(a1,a+8);
22
+ if( (p<0 && a1>a0) || (p>0 && a1<a0) ) {
23
+ endian::read_be(a,a0);
24
+ endian::write_be(a0+(p<0?-1:1),a);
25
+ }
26
+ }
27
+
28
+ } //namespace
29
+
30
+ aes_ctr_mode::aes_ctr_mode(byte const* key, int keysize, byte const* iv)
31
+ : key_(key,keysize)
32
+ , pos_(0)
33
+ , buffpos_(0)
34
+ {
35
+ memcpy(iv_,iv,sizeof(iv_));
36
+ key_.encrypt(iv_,buff_);
37
+ }
38
+
39
+ void aes_ctr_mode::crypt(void const* inv, void * outv, size_t length)
40
+ {
41
+ byte const* in = static_cast<byte const*>(inv);
42
+ byte * out = static_cast<byte *>(outv);
43
+ while(length > 0) {
44
+ size_t todo = std::min(length, 16-buffpos_);
45
+ for(size_t i=0; i<todo; ++i) {
46
+ *out++ = *in++ ^ buff_[buffpos_++];
47
+ }
48
+ length -= todo;
49
+ pos_ += todo;
50
+ if(buffpos_ >= 16) {
51
+ for(int i=15; i>=0; --i) {
52
+ if( ++iv_[i] != 0 ) {
53
+ break;
54
+ }
55
+ }
56
+ buffpos_ = 0;
57
+ key_.encrypt(iv_,buff_);
58
+ }
59
+ }
60
+ }
61
+
62
+ void aes_ctr_mode::seek(std::streampos pos)
63
+ {
64
+ auto block = pos_/16, newblock = pos/16;
65
+ buffpos_ = static_cast<size_t>(pos)%16;
66
+ if(block != newblock) {
67
+ add128(iv_,static_cast<int>(newblock-block));
68
+ key_.encrypt(iv_,buff_);
69
+ }
70
+ pos_ = pos;
71
+ }
72
+
73
+ }} //namespace zindorsky::crypto
@@ -0,0 +1,36 @@
1
+ #pragma once
2
+
3
+ #include "steg_defs.h"
4
+ #include <openssl/aes.h>
5
+ #include <ios>
6
+
7
+ namespace zindorsky {
8
+ namespace crypto {
9
+
10
+ class aes {
11
+ public:
12
+ aes(byte const* key, int keysize);
13
+ void encrypt(byte const* in, byte * out) const;
14
+ void decrypt(byte const* in, byte * out) const;
15
+ void rekey(byte const* key, int keysize);
16
+
17
+ private:
18
+ AES_KEY ekey_;
19
+ AES_KEY dkey_;
20
+ };
21
+
22
+ class aes_ctr_mode {
23
+ public:
24
+ aes_ctr_mode(byte const* key, int keysize, byte const* iv);
25
+ void crypt(void const* in, void * out, size_t length);
26
+ void seek(std::streampos pos);
27
+ std::streampos tell() const { return pos_; }
28
+
29
+ private:
30
+ aes key_;
31
+ byte iv_[16], buff_[16];
32
+ std::streampos pos_;
33
+ size_t buffpos_;
34
+ };
35
+
36
+ }} //namespace zindorsky::steganography
@@ -0,0 +1,100 @@
1
+ #include "bmp.h"
2
+ #include "steg_endian.h"
3
+ #include "file_utils.h"
4
+ #include <cstdint>
5
+
6
+ namespace zindorsky {
7
+ namespace steganography {
8
+
9
+ bmp_provider::bmp_provider( filesystem::path const& filename )
10
+ : bmp_provider( utils::load_from_file(filename) )
11
+ {
12
+ }
13
+
14
+ bmp_provider::bmp_provider(byte const* data, size_t size)
15
+ : bmp_provider( byte_vector(data, data+size) )
16
+ {
17
+ }
18
+
19
+ bmp_provider::bmp_provider(byte_vector const& data)
20
+ : bmp_provider(data.data(), data.size())
21
+ {
22
+ }
23
+
24
+ bmp_provider::bmp_provider(byte_vector && data)
25
+ : file_(std::move(data))
26
+ {
27
+ if (file_.size() < 54) {
28
+ throw invalid_carrier();
29
+ }
30
+
31
+ byte const* header = &file_[0];
32
+
33
+ int bits_per_pixel = (int(header[29])<<8) | header[28];
34
+ //only 24-bit BMPs for now (others use a palette, which makes steganography more difficult)
35
+ if( bits_per_pixel != 24 ) {
36
+ throw std::runtime_error("unsupported BMP format");
37
+ }
38
+
39
+ uint32_t data_offset, col_count, row_count;
40
+ endian::read_le(&header[10], data_offset);
41
+ endian::read_le(&header[18], col_count);
42
+ endian::read_le(&header[22], row_count);
43
+
44
+ row_sz_ = (col_count*bits_per_pixel + 7)/8;
45
+ row_count_ = row_count;
46
+ if( row_sz_ % 4 == 0 ) {
47
+ slack_sz_ = 0;
48
+ } else {
49
+ slack_sz_ = 4 - row_sz_%4;
50
+ }
51
+
52
+ data_ = file_.data() + data_offset;
53
+ }
54
+
55
+ provider_t::index_t bmp_provider::size() const
56
+ {
57
+ return row_sz_ * row_count_;
58
+ }
59
+
60
+ byte & bmp_provider::access_indexed_data( index_t index )
61
+ {
62
+ return *(data_ + logical_to_physical(index));
63
+ }
64
+
65
+ byte const& bmp_provider::access_indexed_data( index_t index ) const
66
+ {
67
+ return *(data_ + logical_to_physical(index));
68
+ }
69
+
70
+ byte_vector bmp_provider::commit_to_memory()
71
+ {
72
+ return file_;
73
+ }
74
+
75
+ void bmp_provider::commit_to_file(filesystem::path const& file)
76
+ {
77
+ utils::save_to_file(file, file_);
78
+ }
79
+
80
+ byte_vector bmp_provider::salt() const
81
+ {
82
+ byte salt[8]={0};
83
+ for(std::size_t i=0; i<row_count_; ++i) {
84
+ salt[ i%sizeof(salt) ] += access_indexed_data(i*row_sz_ + i%row_sz_)>>1;
85
+ }
86
+
87
+ return byte_vector(salt,salt+sizeof(salt));
88
+ }
89
+
90
+ std::size_t bmp_provider::logical_to_physical( provider_t::index_t index ) const
91
+ {
92
+ if( slack_sz_ == 0 ) {
93
+ return static_cast<std::size_t>(index);
94
+ }
95
+ std::size_t row = static_cast<std::size_t>(index / row_sz_), col = static_cast<std::size_t>(index % row_sz_);
96
+ return row*(row_sz_+slack_sz_) + col;
97
+ }
98
+
99
+ }} //namespace zindorsky::steganography
100
+
@@ -0,0 +1,39 @@
1
+ #pragma once
2
+
3
+ #include "provider.h"
4
+ #include <vector>
5
+
6
+ namespace zindorsky {
7
+ namespace steganography {
8
+
9
+ class bmp_provider : public provider_t {
10
+ public:
11
+ explicit bmp_provider( filesystem::path const& filename );
12
+ bmp_provider(byte const* data, size_t size);
13
+ explicit bmp_provider(byte_vector const& data);
14
+ explicit bmp_provider(byte_vector && data);
15
+ //Copyable
16
+ bmp_provider(bmp_provider const&) = default;
17
+ bmp_provider & operator = (bmp_provider const&) = default;
18
+ //Movable
19
+ bmp_provider(bmp_provider &&) = default;
20
+ bmp_provider & operator = (bmp_provider &&) = default;
21
+
22
+ static std::string format() { return "BMP"; }
23
+
24
+ virtual index_t size() const override;
25
+ virtual byte & access_indexed_data( index_t index ) override;
26
+ virtual byte const& access_indexed_data( index_t index ) const override;
27
+ virtual byte_vector commit_to_memory() override;
28
+ virtual void commit_to_file(filesystem::path const& file) override;
29
+ virtual byte_vector salt() const override;
30
+
31
+ private:
32
+ byte_vector file_;
33
+ byte *data_;
34
+ std::size_t row_sz_, row_count_, slack_sz_;
35
+
36
+ std::size_t logical_to_physical( index_t index ) const;
37
+ };
38
+
39
+ }} //namespace zindorsky::steganography
@@ -0,0 +1,244 @@
1
+ #include "device.h"
2
+ #include <cassert>
3
+ #include "steg_endian.h"
4
+
5
+ namespace zindorsky {
6
+ namespace steganography {
7
+
8
+ enum { max_length_sz = 9, nybble_span = 15, byte_span = nybble_span*2, };
9
+
10
+ device_t::device_t( filesystem::path const& carrier_file, std::string const& password, bool open_existing_payload, bool throw_on_open_existing_fail )
11
+ : device_t( provider_t::load(carrier_file), password, open_existing_payload, throw_on_open_existing_fail )
12
+ {
13
+ carrier_file_ = carrier_file;
14
+ }
15
+
16
+ device_t::device_t( std::unique_ptr<provider_t> provider, std::string const& password, bool open_existing_payload, bool throw_on_open_existing_fail )
17
+ : provider_( std::move(provider) )
18
+ , shuffler_(provider_->size() / nybble_span, crypto::key_generator(password,provider_->salt()))
19
+ , max_sz_( provider_->size() / byte_span - max_length_sz )
20
+ , payload_sz_( 0 )
21
+ , pos_(0)
22
+ , dirty_(false)
23
+ {
24
+ if (!provider_) {
25
+ throw invalid_carrier();
26
+ }
27
+
28
+ if( max_sz_ <= 0 ) {
29
+ throw payload_extraction_error();
30
+ }
31
+ if( open_existing_payload ) {
32
+ payload_sz_ = read_payload_length(throw_on_open_existing_fail);
33
+ }
34
+ if( payload_sz_ > max_sz_ ) {
35
+ if (throw_on_open_existing_fail) {
36
+ throw payload_extraction_error();
37
+ } else {
38
+ payload_sz_ = 0;
39
+ }
40
+ }
41
+ }
42
+
43
+ std::streamsize device_t::read(char * s, std::streamsize n)
44
+ {
45
+ if(!s || n<=0) {
46
+ return 0;
47
+ }
48
+ if( pos_ >= payload_sz_ ) {
49
+ return std::char_traits<char_type>::eof();
50
+ }
51
+ std::streamsize r=0;
52
+ while(n > 0 && pos_ < payload_sz_) {
53
+ *s = static_cast<char>(get_byte(pos_));
54
+ ++s;
55
+ pos_ += 1;
56
+ ++r;
57
+ --n;
58
+ }
59
+ return r;
60
+ }
61
+
62
+ std::streamsize device_t::write(char const* s, std::streamsize n)
63
+ {
64
+ if(!s || n<=0) {
65
+ return 0;
66
+ }
67
+ if( pos_ >= max_sz_ ) {
68
+ return std::char_traits<char_type>::eof();
69
+ }
70
+ std::streamsize r=0;
71
+ while(n > 0 && pos_ < max_sz_) {
72
+ byte b = static_cast<byte>(*s);
73
+ put_byte(b,pos_);
74
+ ++s;
75
+ pos_ += 1;
76
+ ++r;
77
+ --n;
78
+ }
79
+ if( pos_ > payload_sz_ ) {
80
+ payload_sz_ = pos_;
81
+ dirty_ = true;
82
+ }
83
+ return r;
84
+ }
85
+
86
+ std::streampos device_t::seek(std::streamoff off, std::ios::seekdir way)
87
+ {
88
+ std::streampos newpos;
89
+ switch(way) {
90
+ case std::ios::cur: newpos = pos_; break;
91
+ case std::ios::end: newpos = payload_sz_; break;
92
+ default: newpos = 0; break;
93
+ }
94
+ newpos += off;
95
+ if( newpos < 0 ) {
96
+ throw std::ios::failure("underseek");
97
+ }
98
+ if( newpos > max_sz_ ) {
99
+ newpos = max_sz_;
100
+ }
101
+ return pos_ = newpos;
102
+ }
103
+
104
+ void device_t::close()
105
+ {
106
+ if (dirty_ && !carrier_file_.empty()) {
107
+ write_to_file(carrier_file_);
108
+ dirty_ = false;
109
+ }
110
+ }
111
+
112
+ void device_t::flush()
113
+ {
114
+ if (dirty_ && !carrier_file_.empty()) {
115
+ write_to_file(carrier_file_);
116
+ dirty_ = false;
117
+ }
118
+ }
119
+
120
+ void device_t::write_to_file(filesystem::path const& outfile)
121
+ {
122
+ if (dirty_) {
123
+ write_payload_length();
124
+ }
125
+ provider_->commit_to_file(outfile);
126
+ dirty_ = false;
127
+ }
128
+
129
+ byte_vector device_t::write_to_memory()
130
+ {
131
+ if (dirty_) {
132
+ write_payload_length();
133
+ }
134
+ dirty_ = false;
135
+ return provider_->commit_to_memory();
136
+ }
137
+
138
+ byte device_t::get_byte(std::streampos const& pos, provider_t::index_t * lo_start, provider_t::index_t * hi_start) const
139
+ {
140
+ assert( pos < max_sz_ + max_length_sz );
141
+
142
+ provider_t::index_t index = shuffler_[pos*2]*nybble_span;
143
+ if(lo_start) { *lo_start = index; }
144
+
145
+ byte cl=0;
146
+ for(byte i=1; i<=nybble_span; ++i) {
147
+ if( provider_->access_indexed_data( index++ ) & 1 ) {
148
+ cl ^= i;
149
+ }
150
+ }
151
+
152
+ index = shuffler_[pos*2+1]*nybble_span;
153
+ if(hi_start) { *hi_start = index; }
154
+
155
+ byte ch=0;
156
+ for(byte i=1; i<=nybble_span; ++i) {
157
+ if( provider_->access_indexed_data( index++ ) & 1 ) {
158
+ ch ^= i;
159
+ }
160
+ }
161
+ return (ch<<4)|cl;
162
+ }
163
+
164
+ void device_t::put_byte(byte b, std::streampos const& pos)
165
+ {
166
+ assert( pos < max_sz_ + max_length_sz );
167
+
168
+ provider_t::index_t lo_start, hi_start;
169
+ byte c = get_byte(pos,&lo_start,&hi_start);
170
+ byte cl = c&15, ch = c>>4, bl = b&15, bh = b>>4;
171
+ if(bl != cl) {
172
+ provider_->access_indexed_data( lo_start + (bl^cl) - 1 ) ^= 1;
173
+ dirty_ = true;
174
+ }
175
+ if(bh != ch) {
176
+ provider_->access_indexed_data( hi_start + (bh^ch) - 1 ) ^= 1;
177
+ dirty_ = true;
178
+ }
179
+ }
180
+
181
+ std::streamsize device_t::truncate()
182
+ {
183
+ if(payload_sz_ != pos_) {
184
+ dirty_ = true;
185
+ }
186
+ return payload_sz_ = pos_;
187
+ }
188
+
189
+ std::streamsize device_t::read_payload_length(bool throw_on_fail) const
190
+ {
191
+ std::streampos pos = max_sz_+max_length_sz-1;
192
+ std::streamsize sz = 0;
193
+ unsigned int shift=0;
194
+ byte b;
195
+ do {
196
+ if(shift+7 > sizeof(std::streampos)*8) {
197
+ if (throw_on_fail) {
198
+ throw payload_extraction_error();
199
+ } else {
200
+ return 0;
201
+ }
202
+ }
203
+ b = get_byte(pos);
204
+ pos -= 1;
205
+ sz |= static_cast<std::streampos>(b&0x7f)<<shift;
206
+ shift += 7;
207
+ } while(b&0x80);
208
+
209
+ return sz;
210
+ }
211
+
212
+ void device_t::write_payload_length()
213
+ {
214
+ if (payload_sz_ < 0) {
215
+ throw payload_extraction_error();
216
+ }
217
+ std::streampos pos = max_sz_+max_length_sz-1;
218
+ std::streamsize sz = payload_sz_;
219
+ do {
220
+ byte b = static_cast<byte>( sz&0x7f );
221
+ sz >>= 7;
222
+ if( sz > 0 ) {
223
+ b |= 0x80;
224
+ }
225
+ put_byte(b,pos);
226
+ pos -= 1;
227
+ } while( sz>0 );
228
+ }
229
+
230
+ byte_vector device_t::salt_for_encryption() const
231
+ {
232
+ if (!provider_) {
233
+ return{};
234
+ }
235
+ byte_vector salt = provider_->salt();
236
+ //Though it's probably fine, for safety, make the salt different than the salt used by the shuffler.
237
+ if(!salt.empty()) {
238
+ salt.front() += 1;
239
+ }
240
+ return salt;
241
+ }
242
+
243
+ }} //namespace zindorsky::steganography
244
+