unveiler 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/unveiler +67 -0
  3. data/lib/unveiler.rb +121 -0
  4. metadata +47 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: deb8e270b0525876f90650a279865f89b2fda044
4
+ data.tar.gz: 637a0e13085f4577cc8eb616bcf74899763d5b88
5
+ SHA512:
6
+ metadata.gz: 46c3da0e933f6c31eee9cbe158dda816566a18cd4ff0c875de99b9f19d6163878b698e9275a1d13d6122623e287d8a517aa371dbd6c0f9c24fa537cd8bf3c4d3
7
+ data.tar.gz: 50ecc0fbfaa9db8f5e037a9ad09b81bbda9b63257bdcf77331d90cc67223ff5bffc43cc21ecf838504d219c2d1da459e0e73b6b55f1e831b334bdd42866e1b32
data/bin/unveiler ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+ require 'unveiler'
3
+
4
+ # Encodes the input file with the specified data, saving the manipulated data
5
+ # to the output file.
6
+ #
7
+ # +input+:: File to be encoded
8
+ # +output+:: File to write encoded data to
9
+ # +data+:: Data to be encoded
10
+ def encode(input, output, data)
11
+ begin
12
+ target = read_file(input)
13
+ data = Unveiler.encode(target, data)
14
+ write_file(output, data)
15
+ rescue
16
+ puts $!
17
+ end
18
+ end
19
+
20
+ # Decodes the provided input file and saves the decoded data to the output
21
+ # file.
22
+ #
23
+ # +input+:: File to be decoded
24
+ # +output+:: File to write decoded data to
25
+ def decode(input, output)
26
+ begin
27
+ target = read_file(input)
28
+ data = Unveiler.decode(target)
29
+ write_file(output, data + "\n")
30
+ rescue
31
+ puts $!.message
32
+ end
33
+ end
34
+
35
+ # Reads data from the specified file.
36
+ #
37
+ # +input+:: File to read
38
+ def read_file(input)
39
+ File.open(input, 'rb'){|file| return file.read}
40
+ end
41
+
42
+ # Writes data to the specified file.
43
+ #
44
+ # +output+:: File to write
45
+ def write_file(output, data)
46
+ File.open(output, 'wb'){|file| file.write(data)}
47
+ end
48
+
49
+ # Prints unveiler usage examples.
50
+ def usage
51
+ puts 'Usage:'
52
+ puts 'unveiler encode [input_file] [output_file] [data]'
53
+ puts 'unveiler decode [input_file] [output_file]'
54
+ end
55
+
56
+ # Parses ARGV to ensure the correct parameters are given.
57
+ def parse_argv
58
+ if ARGV.first == 'encode'
59
+ ARGV.length == 4 ? encode(ARGV[1], ARGV[2], ARGV[3]) : usage
60
+ elsif ARGV.first == 'decode'
61
+ ARGV.length == 3 ? decode(ARGV[1], ARGV[2]) : usage
62
+ else
63
+ usage
64
+ end
65
+ end
66
+
67
+ ARGV.empty? ? usage : parse_argv
data/lib/unveiler.rb ADDED
@@ -0,0 +1,121 @@
1
+ require 'base64'
2
+
3
+ # Unveiler is a basic steganography tool that encodes data by manipulating
4
+ # existing binary data and can decode previously encoded data.
5
+ #
6
+ # Author:: Brendan Goodenough
7
+ # Copyright:: Copyright (c) 2016 Brendan Goodenough
8
+ # License:: MIT
9
+ #
10
+ # The encoding of the data is achieved by manipulating the least significant
11
+ # bits of each byte in the target data. These bits are replaced with the next
12
+ # sequential bit of the data to be encoded, looping until the end of the data
13
+ # is reached.
14
+ #
15
+ # Decoding is achieved by looping through each byte of the target data, and
16
+ # stringing together the least significant bits of each byte. This is repeated
17
+ # until the last 3 bytes in the string of bits forms the phrase 'EOF'.
18
+
19
+ class Unveiler
20
+ # Encodes the specified string of data into the target data. Upon success,
21
+ # a UTF-8 string containing the manipulated data will be returned.
22
+ #
23
+ # +target+:: Target data to be manipulated
24
+ # +data+:: Data to encode
25
+ def self.encode(target, data)
26
+ raise ArgumentError, 'target must be a string' unless target.is_a? String
27
+ raise ArgumentError, 'data must be a string' unless data.is_a? String
28
+
29
+ # Convert target data into an array of bytes
30
+ bytes = target.unpack('B*')[0].scan(/.{8}/)
31
+
32
+ # Convert data to binary
33
+ data = Base64.encode64(data)
34
+ data += 'EOF'
35
+ data = data.unpack('B*').first
36
+
37
+ # Encode the data into the array of bytes
38
+ bytes = self.manipulate_bytes(bytes, data)
39
+
40
+ # Convert binary to UTF-8
41
+ bytes.map!{|byte| byte = byte.to_i(2)}
42
+ return bytes.pack('C*').force_encoding('utf-8')
43
+ end
44
+
45
+ # Decodes any data within the specified target data. Upon success, the decoded
46
+ # string will be returned.
47
+ #
48
+ # +target+:: Target data to be decoded
49
+ def self.decode(target)
50
+ raise ArgumentError, 'target must be a String' unless target.is_a? String
51
+
52
+ # Convert the target data into an array of bytes
53
+ bytes = target.unpack('B*')[0].scan(/.{8}/)
54
+
55
+ # Obtain string from the bytes
56
+ bytes = self.process_bytes(bytes)
57
+ return Base64.decode64(bytes)
58
+ end
59
+
60
+ # Manipulates an array of bytes by looping through and replacing the least
61
+ # significant bit of each byte with the next sequential bit in a string of
62
+ # binary data. The loop will break when the end of the data string is
63
+ # reached. Returns the manipulated array of bytes.
64
+ #
65
+ # +bytes+:: Array of bytes to manipulate
66
+ # +data+:: Binary data to insert
67
+ def self.manipulate_bytes(bytes, data)
68
+ count = 0
69
+
70
+ 8.times do |index|
71
+ # Loop through each byte and replace the bit at the given index
72
+ bytes.reverse.each do |byte|
73
+ byte[7 - index] = data[count]
74
+ count += 1
75
+ return bytes if count == data.length
76
+ end
77
+ end
78
+
79
+ # Ran out of space to encode data
80
+ raise 'Input file is too small for the specified data'
81
+ end
82
+
83
+ # Processes the specified array of bytes by looping through each byte and
84
+ # appending the least significant bit onto a string of bits. At the end of
85
+ # every eighth bit, a check will be made to see if the last 3 bytes of the
86
+ # string contain the phrase 'EOF'. Only the bytes before EOF will be returned
87
+ # if the phrase is found. An error will be raised if no EOF is found.
88
+ #
89
+ # +bytes+:: Array of bytes to be processed
90
+ def self.process_bytes(bytes)
91
+ bits = ''
92
+
93
+ 8.times do |index|
94
+ bytes.reverse.each do |byte|
95
+ # Least significant bit
96
+ bits += byte[7 - index]
97
+
98
+ # Check whether a full byte has been read
99
+ if bits.length % 8 == 0
100
+ # Convert bits to array of bytes
101
+ data = bits.scan(/.{8}/)
102
+
103
+ if data.length >= 3
104
+ # Convert bytes to a UTF-8 string
105
+ data.map!{|b| byte = b.to_i(2)}
106
+ data = data.pack('C*').force_encoding('utf-8')
107
+ len = data.length
108
+
109
+ if data[-3,3] == 'EOF'
110
+ # Return the UTF-8 string, excluding 'EOF'
111
+ return data[0..len - 3]
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ # No EOF was found, error
119
+ raise 'No phrase matching "EOF" was found'
120
+ end
121
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unveiler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Brendan Goodenough
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-22 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Unveiler encodes hidden messages within files by manipulating their least
14
+ significant bits.
15
+ email: brendan@goodenough.nz
16
+ executables:
17
+ - unveiler
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - bin/unveiler
22
+ - lib/unveiler.rb
23
+ homepage: https://github.com/nerdenough/unveiler
24
+ licenses:
25
+ - MIT
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.5.1
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: Encode hidden messages in files
47
+ test_files: []