crypto_toolchain 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +51 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/Guardfile +15 -0
- data/LICENSE +21 -0
- data/README.md +95 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/crypto_toolchain.gemspec +33 -0
- data/exe/crypto +7 -0
- data/lib/crypto_toolchain/black_boxes/aes_ctr_editor.rb +25 -0
- data/lib/crypto_toolchain/black_boxes/cbc_bitflip_target.rb +33 -0
- data/lib/crypto_toolchain/black_boxes/cbc_iv_equals_key_target.rb +35 -0
- data/lib/crypto_toolchain/black_boxes/cbc_padding_oracle.rb +44 -0
- data/lib/crypto_toolchain/black_boxes/ctr_bitflip_target.rb +32 -0
- data/lib/crypto_toolchain/black_boxes/dsa_keypair.rb +50 -0
- data/lib/crypto_toolchain/black_boxes/ecb_cut_and_paste_target.rb +50 -0
- data/lib/crypto_toolchain/black_boxes/ecb_interpolate_chosen_plaintext_oracle.rb +28 -0
- data/lib/crypto_toolchain/black_boxes/ecb_or_cbc_encryptor.rb +47 -0
- data/lib/crypto_toolchain/black_boxes/ecb_prepend_chosen_plaintext_oracle.rb +23 -0
- data/lib/crypto_toolchain/black_boxes/md4_mac.rb +20 -0
- data/lib/crypto_toolchain/black_boxes/mt_19937_stream_cipher.rb +47 -0
- data/lib/crypto_toolchain/black_boxes/netcat_cbc_padding_oracle.rb +33 -0
- data/lib/crypto_toolchain/black_boxes/rsa_keypair.rb +83 -0
- data/lib/crypto_toolchain/black_boxes/rsa_parity_oracle.rb +14 -0
- data/lib/crypto_toolchain/black_boxes/rsa_unpadded_message_recovery_oracle.rb +24 -0
- data/lib/crypto_toolchain/black_boxes/sha1_mac.rb +20 -0
- data/lib/crypto_toolchain/black_boxes.rb +22 -0
- data/lib/crypto_toolchain/diffie_hellman/messages.rb +53 -0
- data/lib/crypto_toolchain/diffie_hellman/mitm.rb +52 -0
- data/lib/crypto_toolchain/diffie_hellman/peer.rb +130 -0
- data/lib/crypto_toolchain/diffie_hellman/peer_info.rb +43 -0
- data/lib/crypto_toolchain/diffie_hellman/received_message.rb +17 -0
- data/lib/crypto_toolchain/diffie_hellman.rb +10 -0
- data/lib/crypto_toolchain/extensions/integer_extensions.rb +90 -0
- data/lib/crypto_toolchain/extensions/object_extensions.rb +24 -0
- data/lib/crypto_toolchain/extensions/string_extensions.rb +263 -0
- data/lib/crypto_toolchain/extensions.rb +8 -0
- data/lib/crypto_toolchain/srp/client.rb +51 -0
- data/lib/crypto_toolchain/srp/framework.rb +55 -0
- data/lib/crypto_toolchain/srp/server.rb +38 -0
- data/lib/crypto_toolchain/srp/simple_client.rb +32 -0
- data/lib/crypto_toolchain/srp/simple_server.rb +68 -0
- data/lib/crypto_toolchain/srp.rb +14 -0
- data/lib/crypto_toolchain/tools/aes_ctr_recoverer.rb +30 -0
- data/lib/crypto_toolchain/tools/cbc_bitflip_attack.rb +30 -0
- data/lib/crypto_toolchain/tools/cbc_iv_equals_key_attack.rb +30 -0
- data/lib/crypto_toolchain/tools/cbc_padding_oracle_attack.rb +51 -0
- data/lib/crypto_toolchain/tools/ctr_bitflip_attack.rb +24 -0
- data/lib/crypto_toolchain/tools/determine_blocksize.rb +20 -0
- data/lib/crypto_toolchain/tools/dsa_recover_nonce_from_signatures.rb +53 -0
- data/lib/crypto_toolchain/tools/dsa_recover_private_key_from_nonce.rb +39 -0
- data/lib/crypto_toolchain/tools/ecb_cut_and_paste_attack.rb +47 -0
- data/lib/crypto_toolchain/tools/ecb_interpolate_chosen_plaintext_attack.rb +72 -0
- data/lib/crypto_toolchain/tools/ecb_prepend_chosen_plaintext_attack.rb +42 -0
- data/lib/crypto_toolchain/tools/interactive_xor.rb +51 -0
- data/lib/crypto_toolchain/tools/low_exponent_rsa_signature_forgery.rb +27 -0
- data/lib/crypto_toolchain/tools/md4_length_extension_attack.rb +30 -0
- data/lib/crypto_toolchain/tools/mt_19937_seed_recoverer.rb +27 -0
- data/lib/crypto_toolchain/tools/mt_19937_stream_cipher_seed_recoverer.rb +40 -0
- data/lib/crypto_toolchain/tools/rsa_broadcast_attack.rb +21 -0
- data/lib/crypto_toolchain/tools/rsa_parity_oracle_attack.rb +33 -0
- data/lib/crypto_toolchain/tools/rsa_unpadded_message_recovery_attack.rb +49 -0
- data/lib/crypto_toolchain/tools/sha1_length_extension_attack.rb +30 -0
- data/lib/crypto_toolchain/tools.rb +31 -0
- data/lib/crypto_toolchain/utilities/hmac.rb +73 -0
- data/lib/crypto_toolchain/utilities/md4.rb +106 -0
- data/lib/crypto_toolchain/utilities/mt_19937.rb +218 -0
- data/lib/crypto_toolchain/utilities/sha1.rb +95 -0
- data/lib/crypto_toolchain/utilities.rb +9 -0
- data/lib/crypto_toolchain/version.rb +3 -0
- data/lib/crypto_toolchain.rb +34 -0
- 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
|