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