ciphr 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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.txt +22 -0
- data/README.md +316 -0
- data/Rakefile +7 -0
- data/TODO +320 -0
- data/bin/ciphr +62 -0
- data/ciphr.gemspec +32 -0
- data/lib/ciphr.rb +35 -0
- data/lib/ciphr/function_registry.rb +47 -0
- data/lib/ciphr/functions.rb +62 -0
- data/lib/ciphr/functions/base_radix.rb +205 -0
- data/lib/ciphr/functions/bitwise.rb +80 -0
- data/lib/ciphr/functions/crypto.rb +44 -0
- data/lib/ciphr/functions/openssl.rb +104 -0
- data/lib/ciphr/functions/reader.rb +44 -0
- data/lib/ciphr/functions/simple.rb +121 -0
- data/lib/ciphr/functions/url.rb +38 -0
- data/lib/ciphr/functions/zlib.rb +94 -0
- data/lib/ciphr/parser.rb +77 -0
- data/lib/ciphr/stream.rb +49 -0
- data/lib/ciphr/version.rb +3 -0
- data/pkg/ciphr-0.0.1.gem +0 -0
- data/spec/ciphr_spec.rb +21 -0
- data/spec/functions_spec.rb +31 -0
- data/spec/randomizer_spec.rb +55 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/stream_spec.rb +38 -0
- data/tests/commands.sh +30 -0
- data/tests/output.r26.2.0.0.bin +0 -0
- data/tests/output.r26.2.1.1.bin +0 -0
- data/tests/output.r30.2.0.0.bin +0 -0
- data/tests/output.r30.2.1.1.bin +0 -0
- data/tests/output.r34.2.0.0.bin +0 -0
- data/tests/output.r34.2.1.1.bin +0 -0
- data/tests/testcase.r26.bin +0 -0
- data/tests/testcase.r30.bin +0 -0
- data/tests/testcase.r34.bin +0 -0
- metadata +173 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module Ciphr::Functions::Bitwise
|
2
|
+
class BinaryTruncBitwise < Ciphr::Functions::Function
|
3
|
+
def apply
|
4
|
+
input,keyinput = @args
|
5
|
+
Proc.new do
|
6
|
+
keychunk = keyinput.read(256)
|
7
|
+
inchunk = input.read(256)
|
8
|
+
if inchunk && keychunk
|
9
|
+
a,b=[inchunk,keychunk].sort_by{|x| x.size}
|
10
|
+
a.bytes.each_with_index.map{|c,i|c.send(@options[:op], b.bytes.to_a[i%b.size])}.pack("c*")
|
11
|
+
else
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.variants
|
18
|
+
[
|
19
|
+
['and-trunc', {:op=>:&}],
|
20
|
+
['or-trunc', {:op=>:|}],
|
21
|
+
[['xor-trunc'], {:op=>:'^'}]
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.params
|
26
|
+
[:input, :input]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class BinaryBitwise < Ciphr::Functions::Function
|
31
|
+
def apply
|
32
|
+
input,keyinput = @args
|
33
|
+
keyb, inputb = [keyinput.read.bytes.to_a, input.read.bytes.to_a].sort_by{|a| a.size }
|
34
|
+
Proc.new do
|
35
|
+
if inputb
|
36
|
+
resb = inputb.each_with_index.map{|c,i|c.send(@options[:op], keyb[i%keyb.size])}
|
37
|
+
res = resb.pack("c*")
|
38
|
+
inputb = nil
|
39
|
+
res
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.variants
|
47
|
+
[
|
48
|
+
['and', {:op=>:&}],
|
49
|
+
['or', {:op=>:|}],
|
50
|
+
[['xor'], {:op=>:'^'}]
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.params
|
55
|
+
[:input, :input]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class UnaryBitwise < Ciphr::Functions::Function
|
60
|
+
def apply
|
61
|
+
input = @args[0]
|
62
|
+
Proc.new do
|
63
|
+
inchunk = input.read(1)
|
64
|
+
if inchunk
|
65
|
+
inchunk.bytes.map{|b| b = ~b }.pack("c*")
|
66
|
+
else
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.variants
|
73
|
+
[ ['not', {}] ]
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.params
|
77
|
+
[:input]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Ciphr::Functions::Crypto
|
2
|
+
class RC4Cipher < Ciphr::Functions::InvertibleFunction
|
3
|
+
def apply
|
4
|
+
input, key = @args
|
5
|
+
keybytes = key.read.unpack('c*')
|
6
|
+
s = (0..255).to_a
|
7
|
+
j = 0
|
8
|
+
(0..255).each do |i|
|
9
|
+
j = (j + s[i] + keybytes[i % keybytes.size]) % 256
|
10
|
+
swp = s[i]
|
11
|
+
s[i] = s[j]
|
12
|
+
s[j] = swp
|
13
|
+
end
|
14
|
+
i = 0
|
15
|
+
j = 0
|
16
|
+
|
17
|
+
$stderr.puts("key: #{keybytes.inspect}")
|
18
|
+
|
19
|
+
Proc.new do
|
20
|
+
byte = input.read(1)
|
21
|
+
if byte
|
22
|
+
i = (i + 1) % 256
|
23
|
+
j = (j + s[i]) % 256
|
24
|
+
swp = s[i]
|
25
|
+
s[i] = s[j]
|
26
|
+
s[j] = swp
|
27
|
+
k = s[(s[i] + s[j]) % 256]
|
28
|
+
m = [(byte.unpack('c*')[0] ^ k)].pack('c*')
|
29
|
+
m
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.variants
|
37
|
+
[[['rc4-ruby'],{}]]
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.params
|
41
|
+
[:input, :key]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module Ciphr::Functions::OpenSSL
|
4
|
+
OPENSSL_DIGESTS = %w(md4 md5 sha sha1 sha224 sha256 sha384 sha512) # no md2
|
5
|
+
#TODO: fail/ignore gracefully with error/warning if openssl unavailable
|
6
|
+
|
7
|
+
class OpenSslDigest < Ciphr::Functions::Function
|
8
|
+
def self.variants
|
9
|
+
OPENSSL_DIGESTS.map{|d| [d, {:variant => d}]}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.params
|
13
|
+
[:input]
|
14
|
+
end
|
15
|
+
|
16
|
+
def apply
|
17
|
+
input = args[0]
|
18
|
+
digester = OpenSSL::Digest.new(@options[:variant])
|
19
|
+
while chunk = input.read(256)
|
20
|
+
digester.update(chunk)
|
21
|
+
end
|
22
|
+
digest = digester.digest
|
23
|
+
Proc.new do
|
24
|
+
d = digest
|
25
|
+
digest = nil
|
26
|
+
d
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class OpenSslHmac < OpenSslDigest
|
32
|
+
def self.variants
|
33
|
+
OPENSSL_DIGESTS.map{|d| [["hmac-#{d}", "hmac#{d}"], {:variant => d}]}
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.params
|
37
|
+
[:input, :key]
|
38
|
+
end
|
39
|
+
|
40
|
+
# reuse code from Digest.apply
|
41
|
+
def apply
|
42
|
+
input, key = @args
|
43
|
+
digester = OpenSSL::HMAC.new(key.read, @options[:variant])
|
44
|
+
while chunk = input.read(256)
|
45
|
+
digester.update(chunk)
|
46
|
+
end
|
47
|
+
digest = digester.digest
|
48
|
+
Proc.new do
|
49
|
+
d = digest
|
50
|
+
digest = nil
|
51
|
+
d
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
class OpenSslCipher < Ciphr::Functions::InvertibleFunction
|
59
|
+
def apply
|
60
|
+
input, key = @args
|
61
|
+
cipher = OpenSSL::Cipher.new(@options[:variant])
|
62
|
+
cipher.send(invert ? :decrypt : :encrypt)
|
63
|
+
cipher.key = key.read
|
64
|
+
random_iv = cipher.random_iv
|
65
|
+
if random_iv.size > 0
|
66
|
+
cipher.iv = invert ? input.read(random_iv.size) : random_iv
|
67
|
+
end
|
68
|
+
Proc.new do
|
69
|
+
if ! invert && random_iv
|
70
|
+
begin
|
71
|
+
random_iv
|
72
|
+
ensure
|
73
|
+
random_iv = nil
|
74
|
+
end
|
75
|
+
else
|
76
|
+
chunk = input.read(256)
|
77
|
+
if cipher
|
78
|
+
if chunk
|
79
|
+
cipher.update(chunk)
|
80
|
+
else
|
81
|
+
begin
|
82
|
+
cipher.final
|
83
|
+
ensure
|
84
|
+
cipher = nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
else
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.variants
|
95
|
+
OpenSSL::Cipher.ciphers.map{|c| c.downcase}.uniq.map do |c|
|
96
|
+
[[c, c.gsub(/-/, "")], {:variant => c}]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.params
|
101
|
+
[:input, :key]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# strictly used by parser classes for literals/wiring
|
2
|
+
# TODO: disable registration
|
3
|
+
|
4
|
+
module Ciphr::Functions::Reader
|
5
|
+
class StringReader < Ciphr::Functions::Function
|
6
|
+
def apply
|
7
|
+
StringProc.new(options[:string])
|
8
|
+
end
|
9
|
+
|
10
|
+
class StringProc #extend Proc?
|
11
|
+
def initialize(str)
|
12
|
+
@str = str
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
begin
|
17
|
+
@str
|
18
|
+
ensure
|
19
|
+
@str = nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class FileReader < Ciphr::Functions::Function
|
26
|
+
def apply
|
27
|
+
f = File.open(options[:file], "r")
|
28
|
+
Proc.new do
|
29
|
+
chunk = f.read(256)
|
30
|
+
f.close if ! chunk
|
31
|
+
chunk
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class IoReader < Ciphr::Functions::Function
|
37
|
+
def apply
|
38
|
+
input = args[0]
|
39
|
+
Proc.new do
|
40
|
+
input.read(256)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Ciphr::Functions::Simple
|
2
|
+
class Cat < Ciphr::Functions::Function
|
3
|
+
def self.variants
|
4
|
+
[[['cat','catenate'], {}]]
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.params
|
8
|
+
[:input]
|
9
|
+
end
|
10
|
+
|
11
|
+
def apply
|
12
|
+
inputs = @args
|
13
|
+
i = 0
|
14
|
+
chunk = nil
|
15
|
+
Proc.new do
|
16
|
+
chunk = inputs[i].read(256)
|
17
|
+
if ! chunk
|
18
|
+
i += 1
|
19
|
+
chunk = inputs[i] && inputs[i].read(256)
|
20
|
+
end
|
21
|
+
#while !(chunk = inputs[i].read(256)) && i < inputs.size
|
22
|
+
# i++
|
23
|
+
#end
|
24
|
+
chunk
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Repack < Ciphr::Functions::Function
|
30
|
+
def apply
|
31
|
+
input, ch1in, ch2in = @args
|
32
|
+
content, ch1, ch2 = [input.read, ch1in.read, ch2in.read]
|
33
|
+
Proc.new do
|
34
|
+
if content
|
35
|
+
begin
|
36
|
+
content.unpack(ch1).pack(ch2)
|
37
|
+
ensure
|
38
|
+
content = nil
|
39
|
+
end
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.variants
|
47
|
+
[ [['repack'], {}] ]
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.params
|
51
|
+
[:input,:ch1,:ch2]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Translate < Ciphr::Functions::Function
|
56
|
+
def apply
|
57
|
+
input, ch1in, ch2in = @args
|
58
|
+
ch1, ch2 = [ch1in.read, ch2in.read]
|
59
|
+
Proc.new do
|
60
|
+
inchunk = input.read(1)
|
61
|
+
if inchunk
|
62
|
+
inchunk.tr(ch1, ch2)
|
63
|
+
else
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.variants
|
70
|
+
[ [['tr','translate'], {}] ]
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.params
|
74
|
+
[:input,:ch1,:ch2]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Replace < Ciphr::Functions::Function
|
79
|
+
def apply
|
80
|
+
input, searchin, replacein = @args
|
81
|
+
search, replace = [searchin.read, replacein.read]
|
82
|
+
buf = ""
|
83
|
+
Proc.new do
|
84
|
+
if buf.size == search.size && search.size > 0
|
85
|
+
buf = ""
|
86
|
+
replace
|
87
|
+
else
|
88
|
+
inchunk = input.read(1)
|
89
|
+
if inchunk
|
90
|
+
if inchunk == search[buf.size]
|
91
|
+
buf += inchunk
|
92
|
+
""
|
93
|
+
else
|
94
|
+
buf += inchunk
|
95
|
+
input.prepend(buf[1,buf.size])
|
96
|
+
ret = buf[0]
|
97
|
+
buf = ""
|
98
|
+
ret
|
99
|
+
end
|
100
|
+
else
|
101
|
+
if buf.size > 0
|
102
|
+
ret = buf
|
103
|
+
buf = ""
|
104
|
+
ret
|
105
|
+
else
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.variants
|
114
|
+
[ [['repl','replace'], {}] ]
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.params
|
118
|
+
[:input,:search,:replace]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Ciphr::Functions::URL
|
4
|
+
#TODO: differentiate between URL and CGI encoding (with '+' char)
|
5
|
+
class UrlEncoding < Ciphr::Functions::InvertibleFunction
|
6
|
+
def apply
|
7
|
+
input = @args[0]
|
8
|
+
if !invert
|
9
|
+
Proc.new do
|
10
|
+
chunk = input.read(1)
|
11
|
+
chunk && CGI.escape(chunk)
|
12
|
+
end
|
13
|
+
else
|
14
|
+
Proc.new do
|
15
|
+
chunk = input.read(1)
|
16
|
+
if (chunk == "%")
|
17
|
+
chunk += input.read(2)
|
18
|
+
chunk && CGI.unescape(chunk)
|
19
|
+
elsif chunk == '+'
|
20
|
+
' '
|
21
|
+
else
|
22
|
+
chunk
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.variants
|
29
|
+
[
|
30
|
+
[['url','uri','cgi'],{}]
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.params
|
35
|
+
[:input]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
3
|
+
module Ciphr::Functions::ZLib
|
4
|
+
class Deflate < Ciphr::Functions::InvertibleFunction
|
5
|
+
def apply
|
6
|
+
input = @args[0]
|
7
|
+
zstream = invert ? Zlib::Inflate.new : Zlib::Deflate.new
|
8
|
+
Proc.new do
|
9
|
+
chunk = input.read(256)
|
10
|
+
if chunk
|
11
|
+
if invert
|
12
|
+
zstream.inflate(chunk)
|
13
|
+
else
|
14
|
+
zstream.deflate(chunk,Zlib::SYNC_FLUSH)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
begin
|
18
|
+
#zstream.finish if invert
|
19
|
+
ensure
|
20
|
+
zstream.close
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.variants
|
27
|
+
[
|
28
|
+
[['deflate'], {}]
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.params
|
33
|
+
[:input]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class Gzip < Ciphr::Functions::InvertibleFunction
|
39
|
+
class UncloseableIOProxy # hack to prevent GzipWriter from closing StringIO
|
40
|
+
def initialize(delegate)
|
41
|
+
@delegate = delegate
|
42
|
+
end
|
43
|
+
|
44
|
+
def method_missing(meth, *args, &block)
|
45
|
+
if meth.to_s != "close"
|
46
|
+
@delegate.send(meth, *args, &block)
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def apply
|
54
|
+
input = @args[0]
|
55
|
+
sio = StringIO.new
|
56
|
+
gz = !invert ? Zlib::GzipWriter.new(UncloseableIOProxy.new(sio)) : Zlib::GzipReader.new(input)
|
57
|
+
Proc.new do
|
58
|
+
if invert # unzip
|
59
|
+
gz.read(256)
|
60
|
+
else # zip
|
61
|
+
chunk = input.read(256)
|
62
|
+
if chunk
|
63
|
+
gz.write chunk
|
64
|
+
sio.rewind
|
65
|
+
ret = sio.read
|
66
|
+
sio.rewind
|
67
|
+
sio.truncate(0)
|
68
|
+
ret
|
69
|
+
elsif gz
|
70
|
+
gz.close
|
71
|
+
gz = nil
|
72
|
+
sio.rewind
|
73
|
+
ret = sio.read
|
74
|
+
sio.rewind
|
75
|
+
sio.truncate(0)
|
76
|
+
ret
|
77
|
+
else
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.variants
|
85
|
+
[
|
86
|
+
[['gzip','gz'], {}]
|
87
|
+
]
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.params
|
91
|
+
[:input]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|