crypto_toolchain 0.1.0

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.
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