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,24 @@
1
+ # encoding; ASCII-8BIT
2
+ module CryptoToolchain
3
+ module Tools
4
+ class CtrBitflipAttack
5
+ def initialize(target: CryptoToolchain::BlackBoxes::CtrBitflipTarget.new)
6
+ @target = target
7
+ end
8
+
9
+ def execute
10
+ easy = ":admin<true:" #only need to flip the last bit of bytes at indices 32, 38, 43
11
+ crypted = target.encrypt(easy)
12
+ crypted.
13
+ flip(7, byte_index: 32).
14
+ flip(7, byte_index: 38).
15
+ flip(7, byte_index: 43)
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :target
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ module DetermineBlocksize
4
+ def blocksize
5
+ return @blocksize if defined?(@blocksize)
6
+ original_size = oracle.encrypt("A").length
7
+ i = 2
8
+ loop do
9
+ plain = "A" * i
10
+ len = oracle.encrypt(plain).length
11
+ if len != original_size
12
+ @blocksize = len - original_size
13
+ return @blocksize
14
+ end
15
+ i += 1
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,53 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ # Recovers private key from message signatures signed with the same nonce (k)
4
+ # This means that they have the same r values
5
+ class DSARecoverNonceFromSignatures
6
+ class Input
7
+ def initialize(r: , s: , message: )
8
+ @r = r.to_i
9
+ @s = s.to_i
10
+ @message = message
11
+ @hash = CryptoToolchain::Utilities::SHA1.hexdigest(message)
12
+ end
13
+ attr_reader :r, :s, :message, :hash
14
+ end
15
+
16
+ def initialize(inputs, q: DSA_Q)
17
+ @targets = targets_for(inputs)
18
+ validate_targets!
19
+ @q = q
20
+ end
21
+ attr_reader :targets, :q
22
+
23
+ def execute(params: true)
24
+ t1 = targets.first
25
+ t2 = targets.last
26
+ m1 = t1.hash.hex
27
+ m2 = t2.hash.hex
28
+ s1 = t1.s
29
+ s2 = t2.s
30
+ # (a + b) mod n = [(a mod n) + (b mod n)] mod n.
31
+ top = (m1 - m2) % q
32
+ k = top * (s1 - s2).invmod(q)
33
+ # numerator = ((m1 % q) - (m2 % q)) % q
34
+ k
35
+ end
36
+
37
+ def validate_targets!
38
+ r1 = targets.first.r
39
+ targets[1..-1].each do |t|
40
+ raise ArgumentError.new("All r-values must be identical") unless t.r == r1
41
+ end
42
+ end
43
+
44
+ def targets_for(inputs)
45
+ inputs.
46
+ group_by {|inp| inp.r }.
47
+ select {|k, v| v.length > 1 }.
48
+ values.
49
+ first
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,39 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ class DSARecoverPrivateKeyFromNonce
4
+ def initialize(public_key: , message: , r: , s: , p: DSA_P, q: DSA_Q, g: DSA_G)
5
+ @public_key = numberize(public_key)
6
+ @p = p
7
+ @q = q
8
+ @g = g
9
+ @r = numberize(r)
10
+ @s = numberize(s)
11
+ @message = message
12
+ end
13
+
14
+ attr_reader :public_key, :message, :r, :s, :p, :q, :g
15
+
16
+ def valid_k?(k)
17
+ x = private_key_from(k: k)
18
+ kp = CryptoToolchain::BlackBoxes::DSAKeypair.new(p: p, q: q, g: g, private_key: x)
19
+ kp.public_key == public_key
20
+ end
21
+
22
+ def private_key_from(k: )
23
+ # (s * k) - H(msg)
24
+ # x = ---------------- mod q
25
+ # r
26
+ numerator = ((s * k) - CryptoToolchain::Utilities::SHA1.digest(message).to_number) % q
27
+ denominator = r.invmod(q)
28
+ ((numerator * denominator) % q).to_bin_string
29
+ end
30
+
31
+ def execute(min: 1, max: 0xffffffff)
32
+ (min..max).each do |k|
33
+ return private_key_from(k: k) if valid_k?(k)
34
+ end
35
+ raise RuntimeError.new("Could not recover key")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,47 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ class EcbCutAndPasteAttack
4
+ include DetermineBlocksize
5
+ def initialize(replace: "user",
6
+ with: "admin",
7
+ oracle: CryptoToolchain::BlackBoxes::EcbCutAndPasteTarget.new,
8
+ initial: "charlesisagood@dog.com"
9
+ )
10
+ @oracle = oracle
11
+ @replace = replace
12
+ @replacement = with
13
+ @initial = initial
14
+ end
15
+
16
+ def execute
17
+ without_text_to_change + replaced_text_only
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :oracle, :replace, :replacement, :initial
23
+
24
+ def without_text_to_change
25
+ (0...Float::INFINITY).each do |i|
26
+ input = initial + "X" * i
27
+ oracle.profile_for(input).in_blocks(blocksize).each do |block|
28
+ if block.start_with?(replace)
29
+ return oracle.encrypt(input).in_blocks(blocksize)[0..-2].join
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def replaced_text_only
36
+ (0...Float::INFINITY).each do |i|
37
+ input = initial + "X" * i + replacement
38
+ oracle.profile_for(input).in_blocks(blocksize).each_with_index do |block, bi|
39
+ if block.start_with?(replacement)
40
+ return oracle.encrypt(input).in_blocks(blocksize)[bi]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: ASCII-8BIT
2
+ module CryptoToolchain
3
+ module Tools
4
+ class EcbInterpolateChosenPlaintextAttack
5
+ include DetermineBlocksize
6
+ PAD = "A".freeze
7
+
8
+ # oracle must return an encrypted string via #encrypt
9
+ def initialize(oracle: CryptoToolchain::BlackBoxes::EcbInterpolateChosenPlaintextOracle.new)
10
+ @oracle = oracle
11
+ unless oracle.encrypt(PAD * blocksize * 10).is_ecb_encrypted?(@blocksize)
12
+ raise ArgumentError.new("Oracle does not appear to encrypt with ECB")
13
+ end
14
+ end
15
+
16
+ def execute
17
+ (0..Float::INFINITY).each_with_object("") do |block_index, solved|
18
+ from_block = (0...blocksize).each_with_object("") do |i, solved_in_block|
19
+ padding_length = blocksize - (solved_in_block.bytes.length) - 1
20
+ padding = PAD * padding_length
21
+ target = oracle_encrypt(padding).in_blocks(blocksize)[block_index + prefix_offset]
22
+ dict = (0..255).map(&:chr).each_with_object({}) do |chr, memo|
23
+ guess = padding + solved + solved_in_block + chr
24
+ output = oracle_encrypt(guess).in_blocks(blocksize)[block_index + prefix_offset]
25
+ memo[output] = chr
26
+ break(memo) if output == target
27
+ end
28
+ if !dict.has_key?(target)
29
+ return "#{solved}#{solved_in_block}"
30
+ end
31
+ solved_in_block << dict.fetch(target)
32
+ end
33
+ solved << from_block
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :oracle
40
+
41
+ def oracle_encrypt(str)
42
+ oracle.encrypt(PAD * prefix_padding_length + str)
43
+ end
44
+
45
+ def prefix_offset
46
+ @prefix_offset ||= large_padding.index(first_repeated_block)
47
+ end
48
+
49
+ def large_padding(large_pad_size = 1024)
50
+ @large_padding ||= oracle.encrypt(PAD * large_pad_size).in_blocks(blocksize)
51
+ end
52
+
53
+ def first_repeated_block
54
+ @first_repeated_block ||= large_padding.
55
+ group_by(&:itself).
56
+ sort_by {|k, v| v.length }.
57
+ last.
58
+ first
59
+ end
60
+
61
+ def prefix_padding_length
62
+ return @prefix_padding_length if defined?(@prefix_padding_length)
63
+ (0..Float::INFINITY).each do |i|
64
+ if oracle.encrypt(PAD * i).in_blocks(blocksize).include?(first_repeated_block)
65
+ @prefix_padding_length = i - blocksize
66
+ return @prefix_padding_length
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: ASCII-8BIT
2
+ module CryptoToolchain
3
+ module Tools
4
+ class EcbPrependChosenPlaintextAttack
5
+ include DetermineBlocksize
6
+ PAD = "A".freeze
7
+
8
+ # oracle must return an encrypted string via #encrypt
9
+ def initialize(oracle: CryptoToolchain::BlackBoxes::EcbPrependChosenPlaintextOracle.new)
10
+ @oracle = oracle
11
+ unless oracle.encrypt(PAD * blocksize * 10).is_ecb_encrypted?(@blocksize)
12
+ raise ArgumentError.new("Oracle does not appear to encrypt with ECB")
13
+ end
14
+ end
15
+
16
+ def execute
17
+ (0..Float::INFINITY).each_with_object("") do |block_index, solved|
18
+ from_block = (0...blocksize).each_with_object("") do |i, solved_in_block|
19
+ padding_length = blocksize - (solved_in_block.bytes.length) - 1
20
+ padding = PAD * padding_length
21
+ target = oracle.encrypt(padding).in_blocks(blocksize)[block_index]
22
+ dict = (0..255).map(&:chr).each_with_object({}) do |chr, memo|
23
+ guess = padding + solved + solved_in_block + chr
24
+ output = oracle.encrypt(guess).in_blocks(blocksize)[block_index]
25
+ memo[output] = chr
26
+ break(memo) if output == target
27
+ end
28
+ if !dict.has_key?(target)
29
+ return "#{solved}#{solved_in_block}"
30
+ end
31
+ solved_in_block << dict.fetch(target)
32
+ end
33
+ solved << from_block
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :oracle
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+ #encoding ASCII-8BIT
2
+ #
3
+ # nonce = 0
4
+ # key = "a'\xC2[R\xEE\x8F2K\xBA\xCA\x980\x9Bb\xD5"
5
+ # _plains = File.read("spec/fixtures/3-19.txt").split("\n").map(&:strip).map(&:from_base64)
6
+ # ciphertexts = _plains.map {|pl| pl.encrypt_ctr(key: key, nonce: nonce, blocksize: 16)}
7
+ # CryptoToolchain::Tools::InteractiveXor.new(ciphertexts).execute
8
+ #
9
+ module CryptoToolchain
10
+ module Tools
11
+ class InteractiveXor
12
+ attr_reader :ciphertexts
13
+ def initialize(ciphertexts)
14
+ @ciphertexts = ciphertexts
15
+ end
16
+
17
+ def execute
18
+ binding.pry
19
+ end
20
+
21
+ def validate_length!(plains)
22
+ len = plains.first.length
23
+ plains.each do |pl|
24
+ raise ArgumentError.new("must have same length") unless pl.length == len
25
+ end
26
+ end
27
+
28
+ def keys_for(plains, index)
29
+ plains.map.with_index do |pl|
30
+ len = [pl.length, ciphertexts[index].length].min
31
+ ciphertexts[index][0...len] ^ pl[0...len]
32
+ end
33
+ end
34
+
35
+ # index is the index of the ciphertext against which you are making an attempt
36
+ # Plains are the plaintexts you want to try as possibilities
37
+ def attempt(index, *plains)
38
+ validate_length!(plains)
39
+ keys = keys_for(plains, index)
40
+ ciphertexts.each_with_index do |ct, i|
41
+ line = keys.map do |k|
42
+ _len = [ct.bytesize, k.bytesize].min
43
+ ct[0..._len] ^ k[0..._len]
44
+ end.join(" | ")
45
+ puts "#{i}\t#{line}"
46
+ end
47
+ nil
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: ASCII-8BIT
2
+ module CryptoToolchain
3
+ module Tools
4
+ class LowExponentRSASignatureForgery
5
+ def initialize(message: , keypair: )
6
+ @keypair = keypair
7
+ @message = message
8
+ end
9
+ attr_reader :keypair, :message
10
+
11
+ def execute
12
+ digest = CryptoToolchain::Utilities::SHA1.digest(message)
13
+ asn = ASN1.fetch(:sha1)
14
+ max = (keypair.bits / 8) - (asn.bytesize + digest.bytesize + 3)
15
+ (1..max).reverse_each do |padlen|
16
+ forged = "\x01\xff\x00#{asn}#{digest}#{0.chr * padlen}".
17
+ to_number.
18
+ root(3, round: :up).
19
+ to_bin_string
20
+ found = keypair.verify(message, signature: forged)
21
+ return forged if found
22
+ end
23
+ raise RuntimeError.new("Couldn't forge a signature")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: ASCII-8BIT
2
+ module CryptoToolchain
3
+ module Tools
4
+ class MD4LengthExtensionAttack
5
+ def initialize(message: , add: , mac: , key_length: )
6
+ @message = message
7
+ @mac = mac
8
+ @add = add
9
+ @key_length = key_length
10
+ end
11
+
12
+ # @return Array [msg, digest] Message that can be validated
13
+ def execute
14
+ dummy_key = "A" * key_length
15
+ padding = CryptoToolchain::Utilities::MD4.padding(dummy_key + message)
16
+ [
17
+ message + padding + add,
18
+ CryptoToolchain::Utilities::MD4.hexdigest(add,
19
+ state: mac,
20
+ append_length: (padding + message + dummy_key).length
21
+ )
22
+ ]
23
+ end
24
+
25
+ attr_reader :message, :mac, :add, :key_length
26
+
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,27 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ class MT19937SeedRecoverer
4
+ def initialize(extracted, start: default_start, finish: Time.now.to_i)
5
+ @extracted = extracted
6
+ @start = start
7
+ @finish = finish
8
+ end
9
+
10
+ def execute
11
+ (start..finish).each do |seed|
12
+ if CryptoToolchain::Utilities::MT19937.new(seed).extract == extracted
13
+ return seed
14
+ end
15
+ end
16
+ raise RuntimeError, "Did not find the seed; consider expanding start and finish"
17
+ end
18
+
19
+ attr_reader :extracted, :start, :finish
20
+
21
+ # One hour ago
22
+ def default_start
23
+ Time.now.to_i - (3600)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ class MT19937StreamCipherSeedRecoverer
4
+ class << self
5
+ def recover_from(ciphertext: , seed: )
6
+ stream = CryptoToolchain::BlackBoxes::MT19937StreamCipher.new(ciphertext, seed: seed)
7
+ stream.decrypt(ciphertext)
8
+ end
9
+
10
+ def valid_token?(tok, start: Time.now.to_i - 5, finish: Time.now.to_i)
11
+ (start..finish).each do |seed|
12
+ if tok == CryptoToolchain::BlackBoxes::MT19937StreamCipher.generate_token(seed: seed)
13
+ return true
14
+ end
15
+ end
16
+ false
17
+ end
18
+ alias_method :valid_token, :valid_token?
19
+ end
20
+
21
+ def initialize(ciphertext: , known: )
22
+ @ciphertext = ciphertext
23
+ @known = known
24
+ end
25
+
26
+ def execute
27
+ (0..CryptoToolchain::BlackBoxes::MT19937StreamCipher::MAX_SEED).each do |seed|
28
+ cipher = CryptoToolchain::BlackBoxes::MT19937StreamCipher.new(ciphertext, seed: seed)
29
+ if cipher.decrypt(ciphertext).include?(known)
30
+ return seed
31
+ end
32
+ end
33
+ raise RuntimeError.new, "Could not recover seed"
34
+ end
35
+
36
+ attr_reader :known, :ciphertext
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ class RSABroadcastAttack
4
+ Input = Struct.new(:ciphertext, :public_key)
5
+ def initialize(inputs)
6
+ @e = inputs.length
7
+ @inputs = inputs
8
+ end
9
+ attr_reader :inputs, :e
10
+
11
+ def execute
12
+ residues = inputs.map(&:ciphertext)
13
+ mods = inputs.map {|i| i.public_key.n }
14
+ result = chinese_remainder(residues, mods)
15
+ result.root(e).
16
+ to_s(16).
17
+ from_hex
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ class RSAParityOracleAttack
4
+ def initialize(oracle: , n: , e: 3)
5
+ @oracle = oracle
6
+ @n = n
7
+ @e = e
8
+ end
9
+ attr_reader :n, :oracle, :e
10
+
11
+ def execute(_ciphertext, output: false)
12
+ ciphertext = _ciphertext.to_number
13
+ min = BigDecimal(0)
14
+ max = BigDecimal(n)
15
+ mid = max/2
16
+ mult = 2.modpow(e, n)
17
+ Math.log2(n).ceil.times do
18
+ mid = (min + max) / 2
19
+ ciphertext = ((ciphertext) * mult) % n
20
+ if oracle.execute(ciphertext.to_bin_string) == 0
21
+ max = mid
22
+ else
23
+ min = mid
24
+ end
25
+ if output
26
+ print "\e[2J\e[f\r#{max.to_i.to_bin_string.gsub(/[^[:print:]]/, '*')}"
27
+ end
28
+ end
29
+ max.to_i.to_bin_string
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ module CryptoToolchain
2
+ module Tools
3
+ class RSAUnpaddedMessageRecoveryAttack
4
+
5
+ attr_reader :oracle, :s
6
+
7
+ def initialize(oracle: , s: 2)
8
+ @oracle = oracle
9
+ @s = s
10
+ end
11
+
12
+ def execute(ciphertext)
13
+ plaintext(
14
+ p_prime(
15
+ c_prime(
16
+ ciphertext.to_number
17
+ )
18
+ )
19
+ )
20
+ end
21
+
22
+ private
23
+
24
+ def e
25
+ oracle.keypair.e
26
+ end
27
+
28
+ def n
29
+ oracle.keypair.public_key.n
30
+ end
31
+
32
+ def c_prime(c)
33
+ (s.modpow(e, n) * c) % n
34
+ end
35
+
36
+ def p_prime(_c_prime)
37
+ oracle.execute(
38
+ _c_prime.to_bin_string
39
+ ).to_number
40
+ end
41
+
42
+ def plaintext(_p_prime)
43
+ (
44
+ (_p_prime * s.invmod(n)) % n
45
+ ).to_bin_string
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: ASCII-8BIT
2
+ module CryptoToolchain
3
+ module Tools
4
+ class SHA1LengthExtensionAttack
5
+ def initialize(message: , add: , mac: , key_length: )
6
+ @message = message
7
+ @mac = mac
8
+ @add = add
9
+ @key_length = key_length
10
+ end
11
+
12
+ # @return Array [msg, digest] Message that can be validated
13
+ def execute
14
+ dummy_key = "A" * key_length
15
+ padding = CryptoToolchain::Utilities::SHA1.padding(dummy_key + message)
16
+ [
17
+ message + padding + add,
18
+ CryptoToolchain::Utilities::SHA1.hexdigest(add,
19
+ state: mac,
20
+ append_length: (padding + message + dummy_key).length
21
+ )
22
+ ]
23
+ end
24
+
25
+ attr_reader :message, :mac, :add, :key_length
26
+
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,31 @@
1
+ require "crypto_toolchain/tools/determine_blocksize"
2
+ require "crypto_toolchain/tools/ecb_prepend_chosen_plaintext_attack"
3
+ require "crypto_toolchain/tools/ecb_interpolate_chosen_plaintext_attack"
4
+ require "crypto_toolchain/tools/ecb_cut_and_paste_attack"
5
+ require "crypto_toolchain/tools/cbc_bitflip_attack"
6
+ require "crypto_toolchain/tools/cbc_padding_oracle_attack"
7
+ require "crypto_toolchain/tools/interactive_xor"
8
+ require "crypto_toolchain/tools/mt_19937_seed_recoverer"
9
+ require "crypto_toolchain/tools/mt_19937_stream_cipher_seed_recoverer"
10
+ require "crypto_toolchain/tools/aes_ctr_recoverer"
11
+ require "crypto_toolchain/tools/ctr_bitflip_attack"
12
+ require "crypto_toolchain/tools/cbc_iv_equals_key_attack"
13
+ require "crypto_toolchain/tools/sha1_length_extension_attack"
14
+ require "crypto_toolchain/tools/md4_length_extension_attack"
15
+ require "crypto_toolchain/tools/rsa_broadcast_attack"
16
+ require "crypto_toolchain/tools/rsa_unpadded_message_recovery_attack"
17
+ require "crypto_toolchain/tools/low_exponent_rsa_signature_forgery"
18
+ require "crypto_toolchain/tools/dsa_recover_private_key_from_nonce"
19
+ require "crypto_toolchain/tools/dsa_recover_nonce_from_signatures"
20
+ require "crypto_toolchain/tools/rsa_parity_oracle_attack"
21
+
22
+ module CryptoToolchain
23
+ module Tools
24
+ def self.detect_single_character_xor(bytestring, non_printable: true)
25
+ arr = non_printable ? (0..255).map(&:chr).to_a : CryptoToolchain::PRINTABLE_CHARACTERS
26
+ arr.sort_by do |chr|
27
+ (chr.repeat_to(bytestring.length) ^ bytestring).score
28
+ end.last
29
+ end
30
+ end
31
+ end