crypto-toolbox 0.1.10 → 0.1.11
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fdd99e3ce62f22f02f795da59cc1f8b543e227b6
|
4
|
+
data.tar.gz: cc192b11d8368034f427a520ce187d4ae22def74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ab6e8349e86a0a8f4e8d59d29071e947e158aca611841a3838f8e79ab9af3697df6842d17a0b3f59c63327607f42036b92de767e65e215e0cda5a42c5e11350
|
7
|
+
data.tar.gz: 3ea4f85090f439fd204a68a644ac44cd4d95978546c208906393252f506bc49e441d9dbeb339ce4ff9789c3a5f28a13ab3fb3766523468db45e488844ba466bf
|
data/lib/crypto-toolbox.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
require 'crypto-toolbox/utils/reporting/console.rb'
|
2
|
+
|
1
3
|
require 'crypto-toolbox/crypt_buffer_input_converter.rb'
|
2
4
|
require 'crypto-toolbox/crypt_buffer.rb'
|
3
5
|
|
4
|
-
|
5
6
|
require 'crypto-toolbox/analyzers/utils/key_filter.rb'
|
6
7
|
require 'crypto-toolbox/analyzers/utils/spell_checker.rb'
|
7
8
|
require 'crypto-toolbox/analyzers/padding_oracle.rb'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Analyzers
|
2
|
+
module Utils
|
3
|
+
class KeyCandidateMap
|
4
|
+
# This class represents a position-index based map of possible values of a key.
|
5
|
+
# Example:
|
6
|
+
# {
|
7
|
+
# 1 => [1,100,22,33]
|
8
|
+
# 2 => [2,77,255]
|
9
|
+
# ...
|
10
|
+
# n => [22,55,222]
|
11
|
+
# }
|
12
|
+
include ::Utils::Reporting::Console
|
13
|
+
|
14
|
+
# factory method for easy use
|
15
|
+
def self.create(input_buf,keylen)
|
16
|
+
new.run(input_buf,keylen)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Algorithm
|
20
|
+
# 1) for each position of the key: (key_byte_pos)
|
21
|
+
# 2) create a stream of all the nth bytes of the keylen
|
22
|
+
# 3) xor any possible byte value (guess) with all nth's bytes
|
23
|
+
# 4) select those guesses that decipher the nth-byte stream to only english plain ascii chars
|
24
|
+
def run(input_buf,keylen)
|
25
|
+
candidate_map ={}
|
26
|
+
(0..(keylen-1)).each do |key_byte_pos|
|
27
|
+
|
28
|
+
# create an array of every nth byte of the input. ( thus a pseudo stream of the nth bytes )
|
29
|
+
# 1) create an enumerator of the nth positions. e.g for iteration 0: [0,7,14,...]
|
30
|
+
# 2) Next: Map the positions to bytes of the input buffer
|
31
|
+
#
|
32
|
+
# NOTE: regular implementation without cryptbuffer magic:
|
33
|
+
# nth_stream = (key_byte_pos).step(input_buf.bytes.length() -1, keylen).map{|i| input_buf.bytes[i]}
|
34
|
+
# nth_byte_stream2 = CryptBuffer.new(nth_stream)
|
35
|
+
|
36
|
+
nth_byte_stream = input_buf.nth_bytes(keylen,offset: key_byte_pos)
|
37
|
+
candidate_map[key_byte_pos] = 0.upto(255).select{|guess| nth_byte_stream.xor_all_with(guess).bytes.all?{|byte| acceptable_char?(byte) } }
|
38
|
+
|
39
|
+
jot("found #{candidate_map[key_byte_pos].inspect} bytes for position: #{key_byte_pos}",debug: true)
|
40
|
+
end
|
41
|
+
candidate_map
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Checks if a given byte maps to a reasonable english language character
|
47
|
+
def acceptable_char?(byte)
|
48
|
+
(byte > 31 && byte < 123) && (byte != 60 && byte !=64)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'crypto-toolbox/analyzers/utils/key_candidate_map.rb'
|
1
2
|
|
2
3
|
=begin
|
3
4
|
# References:
|
@@ -24,22 +25,8 @@ module Analyzers
|
|
24
25
|
#
|
25
26
|
# 4) Do an English language Analysis of the possible result by using
|
26
27
|
# the error rate of the candidate plaintext using hunspell
|
27
|
-
#
|
28
|
-
|
29
28
|
|
30
|
-
|
31
|
-
if debug == false || ENV["DEBUG_ANALYSIS"]
|
32
|
-
puts message
|
33
|
-
end
|
34
|
-
end
|
35
|
-
def print_delimiter_line
|
36
|
-
puts "====================================================================="
|
37
|
-
end
|
38
|
-
|
39
|
-
# Checks if a given byte maps to a reasonable english language character
|
40
|
-
def acceptable_char?(byte)
|
41
|
-
(byte > 31 && byte < 123) && (byte != 60 && byte !=64)
|
42
|
-
end
|
29
|
+
include ::Utils::Reporting::Console
|
43
30
|
|
44
31
|
def find_pattern(buf)
|
45
32
|
bitstring = buf.nth_bits(7).join("")
|
@@ -53,44 +40,20 @@ module Analyzers
|
|
53
40
|
end
|
54
41
|
end.compact.first
|
55
42
|
end
|
56
|
-
|
57
|
-
def create_candidate_map(buf,keylen)
|
58
|
-
candidate_map ={}
|
59
|
-
(0..(keylen-1)).each do |key_byte_pos|
|
60
|
-
|
61
|
-
nth_stream = (key_byte_pos).step(buf.bytes.length() -1, keylen).map{|i| buf.bytes[i]}
|
62
|
-
smart_buf = CryptBuffer.new(nth_stream)
|
63
|
-
|
64
|
-
candidate_map[key_byte_pos]=[]
|
65
|
-
1.upto(255).each do |guess|
|
66
|
-
if smart_buf.xor_all_with(guess).bytes.all?{|byte| acceptable_char?(byte) }
|
67
|
-
jot("YES: " + smart_buf.xor_all_with(guess).to_s,debug: true)
|
68
|
-
candidate_map[key_byte_pos] << guess
|
69
|
-
else
|
70
|
-
# the current byte does not create a plain ascii result ( thus skip it )
|
71
|
-
#jot "NO: " + smart_buf.xor_all_with(guess).to_s
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
candidate_map
|
77
|
-
end
|
78
43
|
|
79
44
|
def analyze(input)
|
80
45
|
buf = CryptBuffer.from_hex(input)
|
81
|
-
|
46
|
+
## === Should this be extracted into a dedicated class ?
|
82
47
|
# Example: "100100" || nil
|
83
48
|
key_pattern = find_pattern(buf)
|
84
|
-
if key_pattern.nil?
|
85
|
-
$stderr.puts "failed to find keylength by ASCII-8-Bit anlysis"
|
86
|
-
exit(1)
|
87
|
-
end
|
88
|
-
keylen = key_pattern.length
|
89
|
-
jot "Found recurring key pattern: #{key_pattern}"
|
90
|
-
jot "Detected key length: #{keylen}"
|
91
49
|
|
50
|
+
assert_key_pattern!(key_pattern)
|
92
51
|
|
93
|
-
|
52
|
+
report_pattern_info(key_pattern)
|
53
|
+
|
54
|
+
##====
|
55
|
+
|
56
|
+
candidate_map = Analyzers::Utils::KeyCandidateMap.create(buf,key_pattern.length)
|
94
57
|
jot "Amount of candidate keys: #{candidate_map.map{|k,v| v.length}.reduce(&:*)}. Starting Permutation (RAM intensive)"
|
95
58
|
|
96
59
|
# split the candidate map into head and*tail to create the prduct of all combinations
|
@@ -99,12 +62,19 @@ module Analyzers
|
|
99
62
|
|
100
63
|
if ENV["DEBUG_ANALYSIS"]
|
101
64
|
ensure_consistent_result!(combinations,candidate_map)
|
102
|
-
print_candidate_decryptions(candidate_map,
|
65
|
+
print_candidate_decryptions(candidate_map,key_pattern.length,buf)
|
103
66
|
end
|
104
67
|
|
105
68
|
results = Analyzers::Utils::KeyFilter::AsciiPlain.new(combinations,buf).filter
|
106
69
|
report_result(results,buf)
|
107
70
|
end
|
71
|
+
private
|
72
|
+
def assert_key_pattern!(key_pattern)
|
73
|
+
if key_pattern.nil?
|
74
|
+
$stderr.puts "failed to find keylength by ASCII-8-Bit anlysis"
|
75
|
+
exit(1)
|
76
|
+
end
|
77
|
+
end
|
108
78
|
|
109
79
|
def ensure_consistent_result!(combinations,condidate_map)
|
110
80
|
# NOTE Consistency check ( enable if you dont trust the generation anymore )
|
@@ -114,6 +84,12 @@ module Analyzers
|
|
114
84
|
end
|
115
85
|
end
|
116
86
|
|
87
|
+
def report_pattern_info(key_pattern)
|
88
|
+
jot "Found recurring key pattern: #{key_pattern}"
|
89
|
+
jot "Detected key length: #{key_pattern.length}"
|
90
|
+
end
|
91
|
+
|
92
|
+
|
117
93
|
def report_result(results,buf)
|
118
94
|
unless results.empty?
|
119
95
|
jot "[Success] Found valid result(s):"
|
@@ -44,6 +44,13 @@ class CryptBuffer
|
|
44
44
|
CryptBufferInputConverter.new.from_hex(input)
|
45
45
|
end
|
46
46
|
|
47
|
+
def nth_bytes(n,offset: 0)
|
48
|
+
return CryptBuffer([]) if n.nil? || n < 1
|
49
|
+
|
50
|
+
CryptBuffer((0+offset).step(length,n).map{|i| bytes[i] }.compact)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
47
54
|
# Returns an array of the nth least sigificant by bit of each byte
|
48
55
|
def nth_bits(n)
|
49
56
|
raise OutOfRangeError if n < 0
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Utils
|
2
|
+
module Reporting
|
3
|
+
module Console
|
4
|
+
# Print to stdout with support of debug conditions
|
5
|
+
# This is especially helpfull if the analysis fails or is too slow
|
6
|
+
def jot(message, debug: false)
|
7
|
+
if debug == false || ENV["DEBUG_ANALYSIS"]
|
8
|
+
puts message
|
9
|
+
end
|
10
|
+
end
|
11
|
+
def print_delimiter_line
|
12
|
+
puts "====================================================================="
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crypto-toolbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dennis Sivia
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aes
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- lib/crypto-toolbox/analyzers/padding_oracle/analyzer.rb
|
53
53
|
- lib/crypto-toolbox/analyzers/padding_oracle/oracles/http_oracle.rb
|
54
54
|
- lib/crypto-toolbox/analyzers/padding_oracle/oracles/tcp_oracle.rb
|
55
|
+
- lib/crypto-toolbox/analyzers/utils/key_candidate_map.rb
|
55
56
|
- lib/crypto-toolbox/analyzers/utils/key_filter.rb
|
56
57
|
- lib/crypto-toolbox/analyzers/utils/spell_checker.rb
|
57
58
|
- lib/crypto-toolbox/analyzers/vigenere_xor.rb
|
@@ -67,6 +68,7 @@ files:
|
|
67
68
|
- lib/crypto-toolbox/crypt_buffer/concerns/random.rb
|
68
69
|
- lib/crypto-toolbox/crypt_buffer/concerns/xor.rb
|
69
70
|
- lib/crypto-toolbox/crypt_buffer_input_converter.rb
|
71
|
+
- lib/crypto-toolbox/utils/reporting/console.rb
|
70
72
|
homepage: https://github.com/scepticulous/crypto-toolbox
|
71
73
|
licenses:
|
72
74
|
- GPLv3
|