crypto-toolbox 0.0.9 → 0.0.10
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 +4 -4
- data/lib/crypto-toolbox/analyzers/vigenere_xor.rb +133 -0
- data/lib/crypto-toolbox/ciphers/caesar.rb +13 -0
- data/lib/crypto-toolbox.rb +2 -0
- metadata +2 -2
- data/lib/crypto-toolbox/break_vigenere.rb +0 -97
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e324bdda406627ce87188d30930b2345145c88ff
|
4
|
+
data.tar.gz: d263f209f0a9df40397b465de3bd7bd039e8288d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a788c9078e6580af052307b8781def44af3d6154c3dc325f30ebb9b769af18a8925f20d26bdd1ad8c93a9e3a66cc3874f21b33587a3940974cd1a38b46fade0
|
7
|
+
data.tar.gz: 1ef771f00df72e37dd20a834ddca7ada6a7f11a4fac03f701cbdf46d421f606d6a4ad00c17ca059dc3d2fe9f9cad619ca5f745108bcfc42b53f61e59721913bf
|
@@ -0,0 +1,133 @@
|
|
1
|
+
|
2
|
+
#require 'shellwords'
|
3
|
+
require 'ffi/hunspell'
|
4
|
+
|
5
|
+
=begin
|
6
|
+
# References:
|
7
|
+
#
|
8
|
+
# http://www.ulduzsoft.com/2015/03/breaking-the-vigenere-cipher/
|
9
|
+
# https://github.com/trekawek/vigenere/blob/master/vig.rb
|
10
|
+
#
|
11
|
+
=end
|
12
|
+
module Analyzers
|
13
|
+
class VigenereXor
|
14
|
+
def jot(message, debug: false)
|
15
|
+
if ENV["SEMI_AUTO_ANALYSIS"]
|
16
|
+
if debug == false || ENV["DEBUG_ANALYSIS"]
|
17
|
+
puts message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
def print_delimiter_line
|
22
|
+
puts "====================================================================="
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_pattern(buf)
|
26
|
+
bitstring = buf.bits.map{|b| b[0]}.join("")
|
27
|
+
1.upto([buf.bytes.length,62].min).map do |ksize|
|
28
|
+
parts = bitstring.scan(/.{#{ksize}}/)
|
29
|
+
if parts.uniq.length == 1
|
30
|
+
parts.first
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end.compact.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def analyze(input)
|
38
|
+
require "crypto-toolbox"
|
39
|
+
require "byebug"
|
40
|
+
|
41
|
+
buf = CryptBuffer.new(input)
|
42
|
+
result = find_pattern(buf)
|
43
|
+
|
44
|
+
if result.nil?
|
45
|
+
$stderr.puts "failed to find keylength by ASCII-8-Bit anlysis"
|
46
|
+
exit(1)
|
47
|
+
end
|
48
|
+
|
49
|
+
keylen = result.length
|
50
|
+
jot "Found recurring key pattern: #{result}"
|
51
|
+
jot "Detected key length: #{keylen}"
|
52
|
+
|
53
|
+
candidate_map ={}
|
54
|
+
(0..(keylen-1)).each do |key_byte|
|
55
|
+
|
56
|
+
nth_stream = (key_byte).step(buf.bytes.length() -1, keylen).map{|i| buf.bytes[i]}
|
57
|
+
smart_buf = CryptBuffer.new(nth_stream)
|
58
|
+
|
59
|
+
candidate_map[key_byte]=[]
|
60
|
+
1.upto(255).each do |possible_key_value|
|
61
|
+
if smart_buf.xor_all_with(possible_key_value).bytes.all?{|e| e > 31 && e < 123 && e != 60 && e !=64}
|
62
|
+
jot("YES: " + smart_buf.xor_all_with(possible_key_value).to_s,debug: true)
|
63
|
+
candidate_map[key_byte] << possible_key_value
|
64
|
+
else
|
65
|
+
# the current byte does not create a plain ascii result ( thus skip it )
|
66
|
+
#jot "NO: " + smart_buf.xor_all_with(possible_key_value).to_s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
head,*tail = candidate_map.map{|k,v|v}
|
72
|
+
|
73
|
+
jot "Amount of candidate keys: #{candidate_map.map{|k,v| v.length}.reduce(&:*)}. Starting Permutation (RAM intensive)"
|
74
|
+
|
75
|
+
combinations = head.product(*tail)
|
76
|
+
# make sure all permutations are still according to the bytes per position map
|
77
|
+
#x = combinations.select do |arr|
|
78
|
+
# #binding.pry
|
79
|
+
# arr.map.with_index{|e,i| candidate_map[i].include?(e) }.all?{|e| e ==true}
|
80
|
+
#end
|
81
|
+
if ENV["SEMI_AUTO_ANALYSIS"] && ENV["DEBUG_ANALYSIS"]
|
82
|
+
print_candidate_encryptions(candidate_map,keylen,buf)
|
83
|
+
end
|
84
|
+
|
85
|
+
result = KeySearch::Filter::AsciiPlain.new(combinations,buf).filter
|
86
|
+
unless result.empty?
|
87
|
+
jot "[Success] Found valid result(s)"
|
88
|
+
result.each do |r|
|
89
|
+
print_delimiter_line
|
90
|
+
jot r.xor(buf).str
|
91
|
+
print_delimiter_line
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
def print_candidate_encryptions(candidate_map,keylen,buf)
|
99
|
+
# printout for debugging. (Manual analysis of the characters)
|
100
|
+
print "======= Decryption result of first #{keylen} bytes with all candidate keys =======\n"
|
101
|
+
(0..keylen-1).each do|i|
|
102
|
+
candidate_map[i].each do |byte|
|
103
|
+
print CryptBuffer.new(buf.bytes[i,keylen]).xor(byte).to_s + " "
|
104
|
+
end
|
105
|
+
print "\n"
|
106
|
+
end
|
107
|
+
print_delimiter_line
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
=begin
|
114
|
+
NOTE: we may at digram and trigram support?
|
115
|
+
#trigram="the "
|
116
|
+
#x = CryptBuffer.new(trigram)
|
117
|
+
=end
|
118
|
+
|
119
|
+
if $0 == __FILE__
|
120
|
+
|
121
|
+
input = ARGV[0] || "0xF96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D963FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794"
|
122
|
+
analyzer = Analyzers::VigenereXor.new.analyze(input)
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
|
@@ -10,7 +10,20 @@ module Ciphers
|
|
10
10
|
def self.decipher(msg,shift)
|
11
11
|
::Ciphers::Caesar.new.decipher(msg,shift)
|
12
12
|
end
|
13
|
+
=begin
|
14
|
+
Within encipher and decipher we use a regexp comparision.
|
15
|
+
Array lookups are must slower and byte comparision is a little faster,
|
16
|
+
but much more complicated
|
13
17
|
|
18
|
+
|
19
|
+
Alphabet letter lookup algorithm comparision:
|
20
|
+
|
21
|
+
Comparison: (see benchmarks/string_comparision.rb)
|
22
|
+
string.bytes.first == A : 3289762.7 i/s
|
23
|
+
string =~ [A-Za-Z] : 2010285.8 i/s - 1.64x slower
|
24
|
+
Letter Array include?(A): 76997.0 i/s - 42.73x slower
|
25
|
+
|
26
|
+
=end
|
14
27
|
def encipher(message,shift)
|
15
28
|
assert_valid_shift!(shift)
|
16
29
|
real_shift = convert_shift(shift)
|
data/lib/crypto-toolbox.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'crypto-toolbox/crypt_buffer.rb'
|
2
2
|
require 'crypto-toolbox/key_filter.rb'
|
3
3
|
require 'crypto-toolbox/spell_checker.rb'
|
4
|
+
|
5
|
+
require 'crypto-toolbox/analyzers/vigenere_xor.rb'
|
4
6
|
require 'crypto-toolbox/ciphers/caesar.rb'
|
5
7
|
require 'crypto-toolbox/ciphers/rot13.rb'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crypto-toolbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dennis Sivia
|
@@ -46,7 +46,7 @@ extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
48
|
- lib/crypto-toolbox.rb
|
49
|
-
- lib/crypto-toolbox/
|
49
|
+
- lib/crypto-toolbox/analyzers/vigenere_xor.rb
|
50
50
|
- lib/crypto-toolbox/ciphers/caesar.rb
|
51
51
|
- lib/crypto-toolbox/ciphers/rot13.rb
|
52
52
|
- lib/crypto-toolbox/crypt_buffer.rb
|
@@ -1,97 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
require_relative './crypt_buffer.rb'
|
3
|
-
require_relative './key_filter.rb'
|
4
|
-
require 'shellwords'
|
5
|
-
require 'ffi/hunspell'
|
6
|
-
|
7
|
-
##
|
8
|
-
# http://www.ulduzsoft.com/2015/03/breaking-the-vigenere-cipher/
|
9
|
-
# https://github.com/trekawek/vigenere/blob/master/vig.rb
|
10
|
-
|
11
|
-
def find_pattern(buf)
|
12
|
-
bitstring = buf.bits.map{|b| b[0]}.join("")
|
13
|
-
1.upto([buf.bytes.length,62].min).map do |ksize|
|
14
|
-
parts = bitstring.scan(/.{#{ksize}}/)
|
15
|
-
if parts.uniq.length == 1
|
16
|
-
parts.first
|
17
|
-
else
|
18
|
-
nil
|
19
|
-
end
|
20
|
-
end.compact.first
|
21
|
-
end
|
22
|
-
|
23
|
-
input = ARGV[0] || "F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D963FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794"
|
24
|
-
|
25
|
-
buf = CryptBuffer.new(input)
|
26
|
-
result = find_pattern(buf)
|
27
|
-
|
28
|
-
if result.nil?
|
29
|
-
$stderr.puts "failed to find keylength by ASCII-8-Bit anlysis"
|
30
|
-
exit(1)
|
31
|
-
end
|
32
|
-
|
33
|
-
keylen = result.length
|
34
|
-
puts "Found recurring key pattern: #{result}"
|
35
|
-
puts "Detected key length: #{keylen}"
|
36
|
-
|
37
|
-
candidate_map ={}
|
38
|
-
(0..(keylen-1)).each do |key_byte|
|
39
|
-
|
40
|
-
nth_stream = (key_byte).step(buf.bytes.length() -1, keylen).map{|i| buf.bytes[i]}
|
41
|
-
smart_buf = CryptBuffer.new(nth_stream)
|
42
|
-
|
43
|
-
candidate_map[key_byte]=[]
|
44
|
-
1.upto(255).each do |possible_key_value|
|
45
|
-
if smart_buf.xor_all_with(possible_key_value).bytes.all?{|e| e > 31 && e < 123 && e != 60 && e !=64}
|
46
|
-
#puts "YES: " + smart_buf.xor_all_with(possible_key_value).to_s
|
47
|
-
candidate_map[key_byte] << possible_key_value
|
48
|
-
else
|
49
|
-
#puts "NO: " + smart_buf.xor_all_with(possible_key_value).to_s
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
head,*tail = candidate_map.map{|k,v|v}
|
57
|
-
|
58
|
-
puts "Amount of candidate keys: #{candidate_map.map{|k,v| v.length}.reduce(&:*)}. Starting Permutation (RAM intensive)"
|
59
|
-
|
60
|
-
combinations = head.product(*tail)
|
61
|
-
# make sure all permutations are still according to the bytes per position map
|
62
|
-
#x = combinations.select do |arr|
|
63
|
-
# #binding.pry
|
64
|
-
# arr.map.with_index{|e,i| candidate_map[i].include?(e) }.all?{|e| e ==true}
|
65
|
-
#end
|
66
|
-
|
67
|
-
# printout for debugging. (Manual analysis of the characters)
|
68
|
-
puts "======= Candidate decryption result of first #{keylen} bytes ======="
|
69
|
-
(0..keylen-1).each do|i|
|
70
|
-
candidate_map[i].each do |byte|
|
71
|
-
print CryptBuffer.new(buf.bytes[i,keylen]).xor(byte).to_s + " "
|
72
|
-
end
|
73
|
-
print "\n"
|
74
|
-
end
|
75
|
-
puts "====================================================================="
|
76
|
-
|
77
|
-
|
78
|
-
result = KeySearch::Filter::AsciiPlain.new(combinations,buf).filter
|
79
|
-
unless result.empty?
|
80
|
-
puts "[Success] Found valid result(s)"
|
81
|
-
result.each do |r|
|
82
|
-
puts r.xor(buf).str
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
=begin
|
87
|
-
NOTE: we may at digram and trigram support?
|
88
|
-
#trigram="the "
|
89
|
-
#x = CryptBuffer.new(trigram)
|
90
|
-
=end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|