crypto_toolchain 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +51 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +3 -0
  6. data/Guardfile +15 -0
  7. data/LICENSE +21 -0
  8. data/README.md +95 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/crypto_toolchain.gemspec +33 -0
  13. data/exe/crypto +7 -0
  14. data/lib/crypto_toolchain/black_boxes/aes_ctr_editor.rb +25 -0
  15. data/lib/crypto_toolchain/black_boxes/cbc_bitflip_target.rb +33 -0
  16. data/lib/crypto_toolchain/black_boxes/cbc_iv_equals_key_target.rb +35 -0
  17. data/lib/crypto_toolchain/black_boxes/cbc_padding_oracle.rb +44 -0
  18. data/lib/crypto_toolchain/black_boxes/ctr_bitflip_target.rb +32 -0
  19. data/lib/crypto_toolchain/black_boxes/dsa_keypair.rb +50 -0
  20. data/lib/crypto_toolchain/black_boxes/ecb_cut_and_paste_target.rb +50 -0
  21. data/lib/crypto_toolchain/black_boxes/ecb_interpolate_chosen_plaintext_oracle.rb +28 -0
  22. data/lib/crypto_toolchain/black_boxes/ecb_or_cbc_encryptor.rb +47 -0
  23. data/lib/crypto_toolchain/black_boxes/ecb_prepend_chosen_plaintext_oracle.rb +23 -0
  24. data/lib/crypto_toolchain/black_boxes/md4_mac.rb +20 -0
  25. data/lib/crypto_toolchain/black_boxes/mt_19937_stream_cipher.rb +47 -0
  26. data/lib/crypto_toolchain/black_boxes/netcat_cbc_padding_oracle.rb +33 -0
  27. data/lib/crypto_toolchain/black_boxes/rsa_keypair.rb +83 -0
  28. data/lib/crypto_toolchain/black_boxes/rsa_parity_oracle.rb +14 -0
  29. data/lib/crypto_toolchain/black_boxes/rsa_unpadded_message_recovery_oracle.rb +24 -0
  30. data/lib/crypto_toolchain/black_boxes/sha1_mac.rb +20 -0
  31. data/lib/crypto_toolchain/black_boxes.rb +22 -0
  32. data/lib/crypto_toolchain/diffie_hellman/messages.rb +53 -0
  33. data/lib/crypto_toolchain/diffie_hellman/mitm.rb +52 -0
  34. data/lib/crypto_toolchain/diffie_hellman/peer.rb +130 -0
  35. data/lib/crypto_toolchain/diffie_hellman/peer_info.rb +43 -0
  36. data/lib/crypto_toolchain/diffie_hellman/received_message.rb +17 -0
  37. data/lib/crypto_toolchain/diffie_hellman.rb +10 -0
  38. data/lib/crypto_toolchain/extensions/integer_extensions.rb +90 -0
  39. data/lib/crypto_toolchain/extensions/object_extensions.rb +24 -0
  40. data/lib/crypto_toolchain/extensions/string_extensions.rb +263 -0
  41. data/lib/crypto_toolchain/extensions.rb +8 -0
  42. data/lib/crypto_toolchain/srp/client.rb +51 -0
  43. data/lib/crypto_toolchain/srp/framework.rb +55 -0
  44. data/lib/crypto_toolchain/srp/server.rb +38 -0
  45. data/lib/crypto_toolchain/srp/simple_client.rb +32 -0
  46. data/lib/crypto_toolchain/srp/simple_server.rb +68 -0
  47. data/lib/crypto_toolchain/srp.rb +14 -0
  48. data/lib/crypto_toolchain/tools/aes_ctr_recoverer.rb +30 -0
  49. data/lib/crypto_toolchain/tools/cbc_bitflip_attack.rb +30 -0
  50. data/lib/crypto_toolchain/tools/cbc_iv_equals_key_attack.rb +30 -0
  51. data/lib/crypto_toolchain/tools/cbc_padding_oracle_attack.rb +51 -0
  52. data/lib/crypto_toolchain/tools/ctr_bitflip_attack.rb +24 -0
  53. data/lib/crypto_toolchain/tools/determine_blocksize.rb +20 -0
  54. data/lib/crypto_toolchain/tools/dsa_recover_nonce_from_signatures.rb +53 -0
  55. data/lib/crypto_toolchain/tools/dsa_recover_private_key_from_nonce.rb +39 -0
  56. data/lib/crypto_toolchain/tools/ecb_cut_and_paste_attack.rb +47 -0
  57. data/lib/crypto_toolchain/tools/ecb_interpolate_chosen_plaintext_attack.rb +72 -0
  58. data/lib/crypto_toolchain/tools/ecb_prepend_chosen_plaintext_attack.rb +42 -0
  59. data/lib/crypto_toolchain/tools/interactive_xor.rb +51 -0
  60. data/lib/crypto_toolchain/tools/low_exponent_rsa_signature_forgery.rb +27 -0
  61. data/lib/crypto_toolchain/tools/md4_length_extension_attack.rb +30 -0
  62. data/lib/crypto_toolchain/tools/mt_19937_seed_recoverer.rb +27 -0
  63. data/lib/crypto_toolchain/tools/mt_19937_stream_cipher_seed_recoverer.rb +40 -0
  64. data/lib/crypto_toolchain/tools/rsa_broadcast_attack.rb +21 -0
  65. data/lib/crypto_toolchain/tools/rsa_parity_oracle_attack.rb +33 -0
  66. data/lib/crypto_toolchain/tools/rsa_unpadded_message_recovery_attack.rb +49 -0
  67. data/lib/crypto_toolchain/tools/sha1_length_extension_attack.rb +30 -0
  68. data/lib/crypto_toolchain/tools.rb +31 -0
  69. data/lib/crypto_toolchain/utilities/hmac.rb +73 -0
  70. data/lib/crypto_toolchain/utilities/md4.rb +106 -0
  71. data/lib/crypto_toolchain/utilities/mt_19937.rb +218 -0
  72. data/lib/crypto_toolchain/utilities/sha1.rb +95 -0
  73. data/lib/crypto_toolchain/utilities.rb +9 -0
  74. data/lib/crypto_toolchain/version.rb +3 -0
  75. data/lib/crypto_toolchain.rb +34 -0
  76. metadata +232 -0
@@ -0,0 +1,263 @@
1
+ # encoding: ASCII-8BIT
2
+ class String
3
+ # Not cryptographically secure
4
+ def self.random_bytes(n)
5
+ n.times.with_object("") do |_, memo|
6
+ memo << random_byte
7
+ end
8
+ end
9
+
10
+ # Obviously not cryptographically secure
11
+ def self.random_byte
12
+ (0..255).to_a.sample.chr
13
+ end
14
+
15
+ def from_hex
16
+ raise StandardError.new("Not hex") unless hex?
17
+ [self].pack("H*")
18
+ end
19
+
20
+ def to_hex
21
+ unpack("H*").first
22
+ end
23
+
24
+ def hex?
25
+ self !~ /[^0-9a-f]/i
26
+ end
27
+
28
+ def swap_endian
29
+ raise ArgumentError.new("Bytesize must be multiple of 4") unless bytesize % 4 == 0
30
+ unpack("L<*").pack("L>*")
31
+ end
32
+
33
+ alias_method :swap_endianness, :swap_endian
34
+
35
+ def to_base64(strict: true)
36
+ if strict
37
+ Base64.strict_encode64(self)
38
+ else
39
+ Base64.encode64(self)
40
+ end
41
+ end
42
+
43
+ def from_base64(strict: true)
44
+ if strict
45
+ begin
46
+ Base64.strict_decode64(self)
47
+ rescue ArgumentError
48
+ Base64.decode64(self)
49
+ end
50
+ else
51
+ Base64.decode64(self)
52
+ end
53
+ end
54
+
55
+ def ^(other)
56
+ if length != other.length
57
+ raise ArgumentError.new("Must be same lengths, self: #{self.bytesize}, other: #{other.bytesize}")
58
+ end
59
+ each_byte.with_index.with_object("") do |(byte, i), ret|
60
+ ret << (byte.ord ^ other[i].ord)
61
+ end
62
+ end
63
+
64
+ def score
65
+ scan(/[etaoin shrdlu]/i).size
66
+ end
67
+
68
+ def in_blocks(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
69
+ bytes.map(&:chr).each_slice(blocksize).map(&:join) || [""]
70
+ end
71
+
72
+ def repeat_to(len)
73
+ ljust(len, self)
74
+ end
75
+
76
+ def to_number
77
+ to_hex.to_i(16)
78
+ end
79
+
80
+ def hamming_distance(other)
81
+ (self ^ other).to_bits.count("1")
82
+ end
83
+
84
+ def to_bits
85
+ self.unpack("B*").first
86
+ end
87
+ alias_method :bitstring, :to_bits
88
+ alias_method :bits, :to_bits
89
+
90
+ def potential_repeating_xor_keysizes(take: 3, min: 2, max: 40)
91
+ (min..max).sort_by do |size|
92
+ normalized_hamming_distance(self.in_blocks(size)) / size.to_f
93
+ end.take(take)
94
+ end
95
+
96
+ def potential_repeating_xor_keys(potential_keysizes: self.potential_repeating_xor_keysizes)
97
+ potential_keysizes.map do |keysize|
98
+ arr = self.in_blocks(keysize)
99
+ transposed = (0...keysize).each_with_object([]) do |i, memo|
100
+ memo << arr.map { |row| row[i] }.join
101
+ end
102
+ key = transposed.each_with_object("") do |str, memo|
103
+ memo << CryptoToolchain::Tools.detect_single_character_xor(str)
104
+ end
105
+ key
106
+ end
107
+ end
108
+
109
+ # unique blocks. block size is in _bytes_
110
+ def unique_blocks(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
111
+ in_blocks(blocksize).each_with_object({}) do |block, found|
112
+ found[block] ||= true
113
+ end.keys
114
+ end
115
+
116
+ def pad_pkcs7(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
117
+ _blocks = in_blocks(blocksize)
118
+ pad_num = blocksize - _blocks.last.bytesize
119
+ if pad_num == 0
120
+ "#{self}#{blocksize.chr * blocksize}"
121
+ else
122
+ "#{self}#{pad_num.chr * pad_num}"
123
+ end
124
+ end
125
+
126
+ def is_pkcs7_padded?(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
127
+ # if self.size != blocksize
128
+ return in_blocks(blocksize).last.is_block_pkcs7_padded?(blocksize)
129
+ # end
130
+ end
131
+
132
+ def without_pkcs7_padding(blocksize = CryptoToolchain::AES_BLOCK_SIZE, raise_error: false)
133
+ if !is_pkcs7_padded?(blocksize)
134
+ raise ArgumentError.new("Not PKCS7 padded") unless is_pkcs7_padded?(blocksize) if raise_error
135
+ return self
136
+ end
137
+ self[0..(bytesize - (1 + bytes.last))]
138
+ end
139
+
140
+ def decrypt_ecb(key: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128')
141
+ in_blocks(blocksize).each_with_object("") do |block, memo|
142
+ dec = OpenSSL::Cipher.new("#{cipher}-ECB")
143
+ dec.decrypt
144
+ dec.key = key
145
+ dec.padding = 0
146
+ plain = dec.update(block) + dec.final
147
+ memo << plain
148
+ end.without_pkcs7_padding(blocksize)
149
+ end
150
+
151
+ def encrypt_ecb(key: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128')
152
+ self.pad_pkcs7(blocksize).in_blocks(blocksize).each_with_object("").with_index do |(block, memo), i|
153
+
154
+ enc = OpenSSL::Cipher.new("#{cipher}-ECB")
155
+ enc.encrypt
156
+ enc.key = key
157
+ enc.padding = 0
158
+ plain = enc.update(block) + enc.final
159
+
160
+ memo << plain
161
+ end
162
+ end
163
+
164
+ def decrypt_cbc(key: , iv: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128', strip_padding: true)
165
+ _blocks = in_blocks(blocksize)
166
+ decrypted = _blocks.each_with_object("").with_index do |(block, memo), i|
167
+ dec = OpenSSL::Cipher.new("#{cipher}-ECB")
168
+ dec.decrypt
169
+ dec.key = key
170
+ dec.padding = 0
171
+ unciphered = dec.update(block) + dec.final
172
+ chain_block = i == 0 ? iv : _blocks[i - 1]
173
+ memo << (unciphered ^ chain_block)
174
+ end
175
+ if strip_padding
176
+ decrypted.without_pkcs7_padding(blocksize)
177
+ else
178
+ decrypted
179
+ end
180
+ end
181
+
182
+ def encrypt_cbc(key: , iv: , blocksize: CryptoToolchain::AES_BLOCK_SIZE, cipher: 'AES-128')
183
+ _blocks = pad_pkcs7(blocksize).in_blocks(blocksize)
184
+ _blocks.each_with_object("").with_index do |(block, memo), i|
185
+ chain_block = i == 0 ? iv : memo[(blocksize * -1)..-1]
186
+ intermediate = block ^ chain_block
187
+ enc = OpenSSL::Cipher.new("#{cipher}-ECB")
188
+ enc.encrypt
189
+ enc.key = key
190
+ enc.padding = 0
191
+ crypted = enc.update(intermediate) + enc.final
192
+ memo << crypted
193
+ end
194
+ end
195
+
196
+ # Bitstring is indexed as a normal string, ie:
197
+ #
198
+ # 'd' = 0x64 = 01100100
199
+ # 01234567
200
+ # 'd'.bitflip(7) => 'e'
201
+ def bitflip(bit_index, byte_index: 0)
202
+ byte_offset, bit_offset = bit_index.divmod(8)
203
+ byte_offset += byte_index
204
+ target = self.dup
205
+ target[byte_offset] = (target[byte_offset].ord ^ (1 << (7-bit_offset))).chr
206
+ target
207
+ end
208
+ alias_method :bit_flip, :bitflip
209
+ alias_method :flipbit, :bitflip
210
+ alias_method :flip_bit, :bitflip
211
+ alias_method :flip, :bitflip
212
+
213
+ def encrypt_ctr(key: , nonce: , cipher: 'AES-128', start_counter: 0)
214
+ each_byte.with_index(start_counter).with_object("") do |(byte, i), memo|
215
+ ctr = i / 16
216
+ ctr_params = [nonce, ctr].pack("Q<Q<")
217
+ enc = OpenSSL::Cipher.new("#{cipher}-ECB")
218
+ enc.encrypt
219
+ enc.key = key
220
+ enc.padding = 0
221
+ keystream = enc.update(ctr_params) + enc.final
222
+ memo << (byte.chr ^ keystream[i % 16])
223
+ end
224
+ end
225
+ alias_method :decrypt_ctr, :encrypt_ctr
226
+
227
+ def contains_duplicate_blocks?(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
228
+ _blocks = in_blocks(blocksize)
229
+ _blocks.length > _blocks.uniq.length
230
+ end
231
+ alias_method :is_ecb_encrypted?, :contains_duplicate_blocks?
232
+
233
+ # Thanks Ruby Facets!
234
+ def snakecase
235
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
236
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
237
+ tr('-', '_').
238
+ gsub(/\s/, '_').
239
+ gsub(/__+/, '_').
240
+ downcase
241
+ end
242
+ alias_method :snake_case, :snakecase
243
+ alias_method :underscore, :snakecase
244
+
245
+ protected
246
+
247
+ def is_block_pkcs7_padded?(blocksize = CryptoToolchain::AES_BLOCK_SIZE)
248
+ return true if self == blocksize.chr * blocksize
249
+ (1...blocksize).each do |padding|
250
+ return true if self[(blocksize - padding)..-1] == padding.chr * padding
251
+ end
252
+ false
253
+ end
254
+
255
+ def normalized_hamming_distance(blocks)
256
+ raise ArgumentError.new("arg should be an array") unless blocks.is_a?(Array)
257
+ (
258
+ blocks[0].hamming_distance(blocks[1]) +
259
+ blocks[0].hamming_distance(blocks[2]) +
260
+ blocks[0].hamming_distance(blocks[3])
261
+ ) / 3.0
262
+ end
263
+ end
@@ -0,0 +1,8 @@
1
+ require "crypto_toolchain/extensions/string_extensions"
2
+ require "crypto_toolchain/extensions/integer_extensions"
3
+ require "crypto_toolchain/extensions/object_extensions"
4
+
5
+ module CryptoToolchain
6
+ module Extensions
7
+ end
8
+ end
@@ -0,0 +1,51 @@
1
+ module CryptoToolchain
2
+ module SRP
3
+ class Client
4
+ include SRP::Framework
5
+
6
+ attr_reader :server_pubkey, :authenticated
7
+ alias_method :authenticated?, :authenticated
8
+
9
+ def initialize(**kargs)
10
+ provided_pubkey = kargs.delete(:pubkey)
11
+ super(**kargs)
12
+ @pubkey = provided_pubkey || g.modpow(privkey, n)
13
+ end
14
+
15
+ def send_hello
16
+ write_message("hello", email, pubkey)
17
+ end
18
+
19
+ def send_verify
20
+ hmac = OpenSSL::HMAC.hexdigest("SHA256", key.to_s, salt.to_s)
21
+ write_message("verify", hmac)
22
+ end
23
+
24
+ def hello_received(_salt, _server_pubkey)
25
+ @salt = _salt.to_i
26
+ @server_pubkey = _server_pubkey.to_i
27
+ secret = calculate_secret
28
+ puts "Client generated secret #{secret}" if DEBUG
29
+ @key = Digest::SHA256.hexdigest(secret.to_s)
30
+ send_verify
31
+ end
32
+
33
+ def authentication_success_received
34
+ @authenticated = true
35
+ write_message("shutdown")
36
+ raise ShutdownSignal
37
+ end
38
+
39
+ def calculate_secret
40
+ return 0 if [0, n, n**2, n**3].include?(pubkey)
41
+
42
+ xH = Digest::SHA256.hexdigest("#{salt}#{password}")
43
+ x = xH.to_i(16)
44
+ uH = Digest::SHA256.hexdigest("#{pubkey}#{server_pubkey}")
45
+ u = uH.to_i(16)
46
+ # S = (B - k * g**x)**(a + u * x) % N
47
+ (server_pubkey - k * g.modpow(x, n)).modpow(privkey + u * x, n)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,55 @@
1
+ module CryptoToolchain
2
+ module SRP
3
+ module Framework
4
+
5
+ attr_reader :n, :g, :k, :email, :password, :socket, :privkey, :pubkey, :salt, :key
6
+
7
+ def initialize(n: CryptoToolchain::NIST_P, g: CryptoToolchain::NIST_G,
8
+ k: 3, email: "charles@goodog.com", password: "i<3porkchops",
9
+ socket: )
10
+ @n = n
11
+ @g = g
12
+ @k = k
13
+ @email = email
14
+ @password = password
15
+ @socket = socket
16
+ @privkey = rand(1..0xffffffff) % n
17
+ end
18
+
19
+ EVENT_WHITELIST = %w( hello verify shutdown error authentication_success ).freeze
20
+ def go!
21
+ event_loop do |event_string|
22
+ event_type, *data = event_string.split(DELIMITER)
23
+ puts "Received #{event_type} #{data}" if DEBUG
24
+ if !EVENT_WHITELIST.include?(event_type)
25
+ socket.puts("error|event #{event_type} unknown") and next
26
+ end
27
+ send("#{event_type}_received", *data)
28
+ end
29
+ end
30
+
31
+ def event_loop
32
+ begin
33
+ loop do
34
+ yield socket.readline.strip
35
+ end
36
+ rescue ShutdownSignal
37
+ # Nothing
38
+ end
39
+ end
40
+
41
+ def shutdown_received(*args)
42
+ raise ShutdownSignal
43
+ end
44
+
45
+ def write_message(*args)
46
+ socket.puts args.join(DELIMITER)
47
+ end
48
+
49
+ def error_received(*args)
50
+ raise StandardError.new(args.join(" "))
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,38 @@
1
+ module CryptoToolchain
2
+ module SRP
3
+ class Server
4
+ include SRP::Framework
5
+
6
+ attr_reader :v, :client_pubkey
7
+
8
+ def initialize(**kargs)
9
+ super(**kargs)
10
+ @salt = rand(1..0xffffffff)
11
+ xH = Digest::SHA256.hexdigest("#{salt}#{password}")
12
+ x = xH.to_i(16)
13
+ @v = g.modpow(x, n)
14
+ @pubkey = k*v + g.modpow(privkey, n)
15
+ end
16
+
17
+ def hello_received(email, _client_pubkey)
18
+ @client_pubkey = _client_pubkey.to_i
19
+ write_message("hello", salt, pubkey)
20
+ uH = Digest::SHA256.hexdigest("#{client_pubkey}#{pubkey}")
21
+ u = uH.to_i(16)
22
+ # S = (A * v**u) ** b % N
23
+ secret = (client_pubkey * v.modpow(u, n)).modpow(privkey, n)
24
+ puts "Server generated secret #{secret}" if DEBUG
25
+ @key = Digest::SHA256.hexdigest(secret.to_s)
26
+ end
27
+
28
+ def verify_received(hmac)
29
+ valid_hmac = OpenSSL::HMAC.hexdigest("SHA256", key.to_s, salt.to_s)
30
+ if hmac == valid_hmac
31
+ write_message("authentication_success")
32
+ else
33
+ write_message("error", "invalid_hmac")
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,32 @@
1
+ module CryptoToolchain
2
+ module SRP
3
+ class SimpleClient < Client
4
+ include SRP::Framework
5
+
6
+ attr_reader :u
7
+
8
+ def initialize(**kargs)
9
+ provided_pubkey = kargs.delete(:pubkey)
10
+ super(**kargs)
11
+ @pubkey = provided_pubkey || g.modpow(privkey, n)
12
+ end
13
+
14
+ def hello_received(_salt, _server_pubkey, _u)
15
+ @salt = _salt.to_i
16
+ @server_pubkey = _server_pubkey.to_i
17
+ @u = _u.to_i
18
+ secret = calculate_secret
19
+ puts "SimpleClient generated secret #{secret}" if DEBUG
20
+ @key = Digest::SHA256.hexdigest(secret.to_s)
21
+ send_verify
22
+ end
23
+
24
+ def calculate_secret
25
+ xH = Digest::SHA256.hexdigest("#{salt}#{password}")
26
+ x = xH.to_i(16)
27
+ # S = B**(a + ux) % n
28
+ server_pubkey.modpow(privkey + (u * x), n)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,68 @@
1
+ module CryptoToolchain
2
+ module SRP
3
+ class SimpleServer < Server
4
+ include SRP::Framework
5
+
6
+ def initialize(n: CryptoToolchain::NIST_P, g: CryptoToolchain::NIST_G,
7
+ k: 3, email: "charles@goodog.com", password: "i<3porkchops",
8
+ privkey: nil, pubkey: nil, u: (rand(1..0x0000ffff)), malicious: false,
9
+ salt: rand(1..0xffffffff), socket: )
10
+ @n = n
11
+ @g = g
12
+ @k = k
13
+ @email = email,
14
+ @password = password
15
+ @socket = socket
16
+ @privkey = privkey || rand(1..0xffffffff) % n
17
+ @pubkey = pubkey || g.modpow(@privkey, n)
18
+ @u = u
19
+ @salt = salt
20
+ xH = Digest::SHA256.hexdigest("#{salt}#{password}")
21
+ x = xH.to_i(16)
22
+ @v = g.modpow(x, n)
23
+ @malicious = malicious
24
+ end
25
+
26
+ attr_reader :salt, :u, :malicious, :recovered_password
27
+ alias_method :malicious?, :malicious
28
+
29
+ def hello_received(email, _client_pubkey)
30
+ @client_pubkey = _client_pubkey.to_i
31
+ write_message("hello", salt, pubkey, u)
32
+ # S = (A * v**u) ** b % N
33
+ secret = (client_pubkey * v.modpow(u, n)).modpow(privkey, n)
34
+ puts "SimpleServer generated secret #{secret}" if DEBUG
35
+ @key = Digest::SHA256.hexdigest(secret.to_s)
36
+ end
37
+
38
+ def wordlist
39
+ return @wordlist if defined? @wordlist
40
+ _words = File.readlines("/usr/share/dict/words").
41
+ shuffle[0...100].
42
+ map(&:strip)
43
+ _words << "i<3porkchops"
44
+ @wordlist = _words.shuffle
45
+ end
46
+
47
+ def crack(hmac)
48
+ wordlist.each_with_index do |word, i|
49
+ _x = Digest::SHA256.hexdigest("#{salt}#{word}").to_i(16)
50
+ _v = g.modpow(_x, n)
51
+ _secret = (client_pubkey * _v.modpow(u, n)).modpow(privkey, n)
52
+ _key = Digest::SHA256.hexdigest(_secret.to_s)
53
+ word_hmac = OpenSSL::HMAC.hexdigest("SHA256", _key, salt.to_s)
54
+ return word if word_hmac == hmac
55
+ end
56
+ nil
57
+ end
58
+
59
+ def verify_received(hmac)
60
+ if malicious?
61
+ @recovered_password = crack(hmac)
62
+ puts "Recovered #{@recovered_password}" if DEBUG
63
+ end
64
+ super(hmac)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,14 @@
1
+ require "crypto_toolchain/srp/framework"
2
+ require "crypto_toolchain/srp/client"
3
+ require "crypto_toolchain/srp/server"
4
+ require "crypto_toolchain/srp/simple_client"
5
+ require "crypto_toolchain/srp/simple_server"
6
+
7
+ module CryptoToolchain
8
+ module SRP
9
+ ShutdownSignal = Class.new(RuntimeError)
10
+
11
+ DELIMITER = "|"
12
+ DEBUG = false
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ class AesCtrRecoverer
4
+
5
+ attr_reader :ciphertext, :editor
6
+
7
+ def initialize(editor)
8
+ @editor = editor
9
+ @ciphertext = editor.ciphertext
10
+ end
11
+
12
+ def execute
13
+ (0...(ciphertext.length)).each_with_object("") do |i, memo|
14
+ memo << get_character(i)
15
+ end
16
+ end
17
+
18
+ def get_character(i)
19
+ (0..255).each do |byte|
20
+ chr = byte.chr
21
+ if editor.edit(offset: i, with: chr) == ciphertext
22
+ return chr
23
+ end
24
+ end
25
+ raise RuntimeError, "Could not recover character"
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # encoding; ASCII-8BIT
2
+ module CryptoToolchain
3
+ module Tools
4
+ class CbcBitflipAttack
5
+ def initialize(target: CryptoToolchain::BlackBoxes::CbcBitflipTarget.new)
6
+ @target = target
7
+ end
8
+
9
+ def flip(block, byte, bit)
10
+ new_byte = ((1 << (bit - 8)) ^ (block[byte].ord)).chr
11
+ block[0...byte] + new_byte + block[(byte+1)..-1]
12
+ end
13
+
14
+ def execute
15
+ easy = ":admin<true:" #only need to flip the last bit of bytes 1, 7, 12
16
+ blocks = target.encrypt(easy).in_blocks(16)
17
+ first = flip(blocks[1], 0, 8)
18
+ equals = flip(first, 6, 8)
19
+ last = flip(equals, 11, 8)
20
+ blocks[1] = last
21
+ blocks.join
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :target
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # encoding; ASCII-8BIT
2
+ module CryptoToolchain
3
+ module Tools
4
+ class CbcIvEqualsKeyAttack
5
+ attr
6
+ def initialize(target: CryptoToolchain::BlackBoxes::CbcIvEqualsKeyTarget.new,
7
+ message_prefix: "Invalid byte in ")
8
+ @target = target
9
+ @message_prefix = message_prefix
10
+ end
11
+
12
+ def execute
13
+ initial = ("A" * CryptoToolchain::AES_BLOCK_SIZE * 3)
14
+ blocks = target.encrypt(initial).in_blocks(CryptoToolchain::AES_BLOCK_SIZE)
15
+ mal = blocks[0] + (0.chr * 16) + blocks[0]
16
+ begin
17
+ target.is_admin?(mal)
18
+ rescue RuntimeError => e
19
+ blocks = e.message[(message_prefix.length)..-1].in_blocks(CryptoToolchain::AES_BLOCK_SIZE)
20
+ blocks[0] ^ blocks[2]
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :target, :message_prefix
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: ASCII-8BIT
2
+ module CryptoToolchain
3
+ module Tools
4
+ class CbcPaddingOracleAttack
5
+ def initialize(oracle: , blocksize: 16)
6
+ @oracle = oracle
7
+ @blocksize = blocksize
8
+ end
9
+
10
+ def execute
11
+ _blocks = ciphertext.in_blocks(blocksize)
12
+ _blocks[1..-1].map.with_index(1) do |block, i|
13
+ preceding = _blocks[i - 1]
14
+ intermediate = intermediate_block(preceding: preceding, target: block)
15
+ intermediate ^ preceding
16
+ end.
17
+ join.
18
+ without_pkcs7_padding(blocksize)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :oracle, :blocksize
24
+
25
+ def ciphertext
26
+ @ciphertext ||= oracle.ciphertext
27
+ end
28
+
29
+ private
30
+
31
+ def intermediate_block(preceding: , target: )
32
+ range = Array(0...blocksize).reverse
33
+ range.each_with_object(0x00.chr * blocksize) do |index, memo|
34
+ pad = blocksize - index
35
+ padding = (pad.chr * pad).rjust(blocksize, 0x00.chr)
36
+ candidate = padding ^ memo ^ preceding
37
+ (0..255).each do |guess|
38
+ next if (preceding.bytes[index] == guess && index == blocksize - 1)
39
+ candidate = padding ^ memo
40
+ candidate[index] = guess.chr
41
+ attempt = candidate + target
42
+ if oracle.execute(attempt)
43
+ memo[index] = (guess ^ pad).chr
44
+ break
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end