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: 2eab51c42b84900135dc165a677a18be4d470a1b
4
- data.tar.gz: a2ec0819114133ae5f613ef2ce4ed153c10a3dff
3
+ metadata.gz: fdd99e3ce62f22f02f795da59cc1f8b543e227b6
4
+ data.tar.gz: cc192b11d8368034f427a520ce187d4ae22def74
5
5
  SHA512:
6
- metadata.gz: 340f89d19a2bb483beffa8b93c16c4c260c6ddb289ce2e187fb53b731413078eb7aa79b8c93b633ad1d5e8e29bde23eded39b7605d873530c0b30c3aff50aaa8
7
- data.tar.gz: 0be69d404f8007cd889051c403e92ab4cf09b0600bb8aa04432115c3042fadcb1505429f873cfbd7625333206826cd12a7ac5bcc036eeedb7c3e2626e1fe5c60
6
+ metadata.gz: 8ab6e8349e86a0a8f4e8d59d29071e947e158aca611841a3838f8e79ab9af3697df6842d17a0b3f59c63327607f42036b92de767e65e215e0cda5a42c5e11350
7
+ data.tar.gz: 3ea4f85090f439fd204a68a644ac44cd4d95978546c208906393252f506bc49e441d9dbeb339ce4ff9789c3a5f28a13ab3fb3766523468db45e488844ba466bf
@@ -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
- def jot(message, debug: false)
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
- candidate_map = create_candidate_map(buf,keylen)
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,keylen,buf)
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.10
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-22 00:00:00.000000000 Z
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