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,73 @@
|
|
1
|
+
module CryptoToolchain
|
2
|
+
module Utilities
|
3
|
+
class HMAC
|
4
|
+
class << self
|
5
|
+
def digest(message, key: , hash: CryptoToolchain::Utilities::SHA1)
|
6
|
+
new(key: key, hash: hash).digest(message)
|
7
|
+
end
|
8
|
+
|
9
|
+
def hexdigest(message, key: , hash: CryptoToolchain::Utilities::SHA1)
|
10
|
+
new(key: key, hash: hash).hexdigest(message)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(key: , hash: , blocksize: nil)
|
16
|
+
@key = key
|
17
|
+
@hash = hash
|
18
|
+
@blocksize = blocksize || determine_blocksize
|
19
|
+
end
|
20
|
+
|
21
|
+
def digest(message)
|
22
|
+
hash.digest(outer_pad + hash.digest(inner_pad + message))
|
23
|
+
end
|
24
|
+
|
25
|
+
def hexdigest(message)
|
26
|
+
digest(message).to_hex
|
27
|
+
end
|
28
|
+
|
29
|
+
def determine_blocksize
|
30
|
+
case hash.to_s.split(':').last.downcase.gsub(/[^a-z0-9]/i, '')
|
31
|
+
when /md(4|5)/
|
32
|
+
64
|
33
|
+
when /sha(1|224|256)/
|
34
|
+
64
|
35
|
+
else
|
36
|
+
raise ArgumentError.new("Unsupported hash #{hash}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def outer_pad
|
43
|
+
@outer_pad ||= (0x5c.chr * blocksize) ^ blocksize_key
|
44
|
+
end
|
45
|
+
|
46
|
+
def inner_pad
|
47
|
+
@inner_pad ||= (0x36.chr * blocksize) ^ blocksize_key
|
48
|
+
end
|
49
|
+
|
50
|
+
def blocksize_key
|
51
|
+
@blocksize_key ||= padded(shortened(key))
|
52
|
+
end
|
53
|
+
|
54
|
+
def padded(input)
|
55
|
+
if input.bytesize < blocksize
|
56
|
+
input.ljust(blocksize, 0.chr)
|
57
|
+
else
|
58
|
+
input
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def shortened(input)
|
63
|
+
if input.bytesize > blocksize
|
64
|
+
hash.digest(input)
|
65
|
+
else
|
66
|
+
input
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_reader :blocksize, :key, :hash
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
#encoding: ASCII-8BIT
|
2
|
+
module CryptoToolchain
|
3
|
+
module Utilities
|
4
|
+
class MD4
|
5
|
+
class << self
|
6
|
+
def hexdigest(str, state: INITIAL_STATE, append_length: 0 )
|
7
|
+
CryptoToolchain::Utilities::MD4.new(str).hexdigest(state: state, append_length: append_length)
|
8
|
+
end
|
9
|
+
|
10
|
+
def bindigest(str, state: INITIAL_STATE, append_length: 0)
|
11
|
+
CryptoToolchain::Utilities::MD4.new(str).bindigest(state: state, append_length: append_length)
|
12
|
+
end
|
13
|
+
alias_method :digest, :bindigest
|
14
|
+
|
15
|
+
def padding(str)
|
16
|
+
num_null_pad = (56 - (str.bytesize + 1) ) % 64
|
17
|
+
0x80.chr + (0.chr * num_null_pad) + [(str.bytesize * 8)].pack("Q<")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(message)
|
22
|
+
@original = message
|
23
|
+
end
|
24
|
+
|
25
|
+
def hexdigest(state: INITIAL_STATE, append_length: 0)
|
26
|
+
bindigest(state: state, append_length: append_length).unpack("H*").join
|
27
|
+
end
|
28
|
+
|
29
|
+
def bindigest(state: INITIAL_STATE, append_length: 0)
|
30
|
+
running_state = registers_from(state)
|
31
|
+
|
32
|
+
length = original.bytesize + append_length
|
33
|
+
|
34
|
+
padding_len = (56 - (length + 1) ) % 64
|
35
|
+
str_length = [(length * 8)].pack("Q<")
|
36
|
+
padding = (0x80.chr + (0.chr * padding_len) + str_length)
|
37
|
+
|
38
|
+
(original + padding).in_blocks(64).each do |block|
|
39
|
+
w = block.unpack("L<16")
|
40
|
+
|
41
|
+
a, b, c, d = running_state
|
42
|
+
# Extraction of each 16-operation round into a loop over four elements originally
|
43
|
+
# found at https://rosettacode.org/wiki/MD4#Ruby
|
44
|
+
[0, 4, 8, 12].each do |i|
|
45
|
+
a = f(a, b, c, d, w[i]).lrot(3)
|
46
|
+
d = f(d, a, b, c, w[i+1]).lrot(7)
|
47
|
+
c = f(c, d, a, b, w[i+2]).lrot(11)
|
48
|
+
b = f(b, c, d, a, w[i+3]).lrot(19)
|
49
|
+
end
|
50
|
+
[0, 1, 2, 3].each do |i|
|
51
|
+
a = g(a, b, c, d, w[i]).lrot(3)
|
52
|
+
d = g(d, a, b, c, w[i+4]).lrot(5)
|
53
|
+
c = g(c, d, a, b, w[i+8]).lrot(9)
|
54
|
+
b = g(b, c, d, a, w[i+12]).lrot(13)
|
55
|
+
end
|
56
|
+
[0, 2, 1, 3].each do |i|
|
57
|
+
a = h(a, b, c, d, w[i]).lrot(3)
|
58
|
+
d = h(d, a, b, c, w[i+8]).lrot(9)
|
59
|
+
c = h(c, d, a, b, w[i+4]).lrot(11)
|
60
|
+
b = h(b, c, d, a, w[i+12]).lrot(15)
|
61
|
+
end
|
62
|
+
|
63
|
+
[a, b, c, d].each_with_index do |val, i|
|
64
|
+
running_state[i] = (running_state[i] + val) & 0xffffffff
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
running_state.pack("L<4")
|
69
|
+
end
|
70
|
+
alias_method :digest, :bindigest
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
attr_reader :original
|
75
|
+
|
76
|
+
def f(arg, x, y, z, block)
|
77
|
+
arg +
|
78
|
+
((x & y) | (~x & z)) +
|
79
|
+
block
|
80
|
+
end
|
81
|
+
|
82
|
+
def g(arg, x, y, z, block)
|
83
|
+
arg +
|
84
|
+
((x & y) | (x & z) | (y & z)) +
|
85
|
+
block +
|
86
|
+
0x5a827999
|
87
|
+
end
|
88
|
+
|
89
|
+
def h(arg, x, y, z, block)
|
90
|
+
arg +
|
91
|
+
(x ^ y ^ z) +
|
92
|
+
block +
|
93
|
+
0x6ed9eba1
|
94
|
+
end
|
95
|
+
|
96
|
+
# Equivalent to [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 ]
|
97
|
+
INITIAL_STATE = "0123456789abcdeffedcba9876543210"
|
98
|
+
|
99
|
+
def registers_from(hex_str)
|
100
|
+
raise ArgumentError.new("Argument must be a hex string") unless hex_str.hex?
|
101
|
+
raise ArgumentError.new("Argument must be 32 characters long") unless hex_str.length == 32
|
102
|
+
hex_str.from_hex.unpack("L<4")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
#encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
# Note: 64 bit is untested
|
4
|
+
module CryptoToolchain
|
5
|
+
module Utilities
|
6
|
+
class MT19937
|
7
|
+
PARAMETERS_32 = {
|
8
|
+
w: 32, n: 624, m: 397, r: 31,
|
9
|
+
a: 0x9908b0df,
|
10
|
+
u: 11, d: 0xFFFFFFFF,
|
11
|
+
s: 7, b: 0x9d2c5680,
|
12
|
+
t: 15, c: 0xefc60000,
|
13
|
+
l: 18,
|
14
|
+
f: 1812433253
|
15
|
+
}.freeze
|
16
|
+
PARAMETERS_64 = {
|
17
|
+
w: 64, n: 312, m: 156, r: 31,
|
18
|
+
a: 0xB5026F5AA96619E9,
|
19
|
+
u: 29, d: 0x5555555555555555,
|
20
|
+
s: 17, b: 0x71D67FFFEDA60000,
|
21
|
+
t: 37, c: 0xFFF7EEE000000000,
|
22
|
+
l: 43,
|
23
|
+
f: 6364136223846793005
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
def self.from_array(arr, bits: 32, index: parameters_for(bits).fetch(:n))
|
27
|
+
mt = new(0, bits: bits)
|
28
|
+
mt.send(:state=, arr)
|
29
|
+
mt.send(:index=, index)
|
30
|
+
mt
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.parameters_for(bits)
|
34
|
+
case bits
|
35
|
+
when 32
|
36
|
+
PARAMETERS_32
|
37
|
+
when 64
|
38
|
+
PARAMETERS_64
|
39
|
+
else
|
40
|
+
raise ArgumentError.new("Bits must be 32 or 64")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(seed, bits: 32)
|
45
|
+
@seed = seed
|
46
|
+
set_vars!(self.class.parameters_for(bits))
|
47
|
+
@index = n
|
48
|
+
@state = build_state!
|
49
|
+
end
|
50
|
+
|
51
|
+
def ==(other)
|
52
|
+
return false unless other.is_a?(self.class)
|
53
|
+
other.send(:state) == state && other.send(:index) == index
|
54
|
+
end
|
55
|
+
|
56
|
+
def extract
|
57
|
+
twist! if index >= n
|
58
|
+
temper(state[index])
|
59
|
+
ensure
|
60
|
+
@index += 1
|
61
|
+
end
|
62
|
+
|
63
|
+
def temper(y)
|
64
|
+
y ^= (y >> u) & d
|
65
|
+
y ^= (y << s) & b
|
66
|
+
y ^= (y << t) & c
|
67
|
+
y ^= (y >> l)
|
68
|
+
lowest_bits(y)
|
69
|
+
end
|
70
|
+
|
71
|
+
def untemper(y)
|
72
|
+
y = untemper_rshift(y, shift: l)
|
73
|
+
y = untemper_lshift(y, shift: t, mask: c)
|
74
|
+
y = untemper_lshift(y, shift: s, mask: b)
|
75
|
+
untemper_rshift(y, shift: u, mask: d)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
attr_reader(*(PARAMETERS_32.keys))
|
81
|
+
attr_accessor :seed, :state, :index
|
82
|
+
|
83
|
+
# General principle:
|
84
|
+
# x ^= (x << y) is periodic.
|
85
|
+
#
|
86
|
+
# What we want to do, then, is exploit this periodicity:
|
87
|
+
# a -> b -> c -> d
|
88
|
+
# ^ v
|
89
|
+
# |------<-------|
|
90
|
+
# Where the temper function went from c -> d, we want to go d->a->b->c to untemper
|
91
|
+
# So we do the most naive thing possible, rather than the performant method below which never quite
|
92
|
+
# clicked in the ol' brain
|
93
|
+
def untemper_lshift(val, shift: , mask: 0xffffffff)
|
94
|
+
original = val
|
95
|
+
loop do
|
96
|
+
prev = val
|
97
|
+
val ^= ((val << shift) & mask)
|
98
|
+
return prev if val == original
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def untemper_rshift(val, shift: , mask: 0xffffffff)
|
103
|
+
original = val
|
104
|
+
loop do
|
105
|
+
prev = val
|
106
|
+
val ^= ((val >> shift) & mask)
|
107
|
+
return prev if val == original
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def defunct_untemper_step_for_posterity(y, debug: false)
|
112
|
+
# We're reversing
|
113
|
+
# y ^= (y << 7) & 0x9d2cf80
|
114
|
+
# so, take the bottom 25 bits of y, shift them over, and & that with the constant 0x9d2c5680,
|
115
|
+
# and xor the result with the top 25 bits of y. Notably, this leaves the bottom 7 bits of y
|
116
|
+
# untouched. The general idea is that we iteratively recover bits. The input shares the bottom 7
|
117
|
+
# bits with the proper output. We work in 7-bit chunks (the last chunk is only 4 bits of course),
|
118
|
+
# shifting and xor-ing (and &ing with the appropriate chunk of the mask) as we go.
|
119
|
+
#
|
120
|
+
# This algorithm never has varying periodicity, unlike the brute force method above. My current
|
121
|
+
# thought is that this is because I'm not appropriately breaking the mask up into chunks like I do
|
122
|
+
# here, but I'm not quite sure. For now, I'll stick with the naive algorithm until I have a complete
|
123
|
+
# understanding of this one.
|
124
|
+
if debug
|
125
|
+
puts " y #{y.to_bits} #{y}"
|
126
|
+
puts
|
127
|
+
puts "y<<7 #{((y<<7) & 0xffffffff).to_bits}"
|
128
|
+
puts "&with #{(0x00003f80 & b).to_bits}"
|
129
|
+
puts " = #{((y << 7) & (0x00003f80 & b)).to_bits}"
|
130
|
+
puts "xor y #{y.to_bits}"
|
131
|
+
end
|
132
|
+
|
133
|
+
y ^= (y << 7) & (0x00003f80 & b)
|
134
|
+
|
135
|
+
if debug
|
136
|
+
puts " = #{y.to_bits}"
|
137
|
+
puts
|
138
|
+
puts "y<<7 #{((y<<7) & 0xffffffff).to_bits}"
|
139
|
+
puts "&with #{(0x001fc000 & b).to_bits}"
|
140
|
+
puts " = #{((y << 7) & (0x001fc000 & b)).to_bits}"
|
141
|
+
puts "xor y #{y.to_bits}"
|
142
|
+
end
|
143
|
+
|
144
|
+
y ^= (y << 7) & (0x001fc000 & b)
|
145
|
+
|
146
|
+
if debug
|
147
|
+
puts " = #{y.to_bits}"
|
148
|
+
puts
|
149
|
+
puts "y<<7 #{((y<<7) & 0xffffffff).to_bits}"
|
150
|
+
puts "&with #{(0x0fe00000 & b).to_bits}"
|
151
|
+
puts " = #{((y << 7) & (0x0fe00000 & b)).to_bits}"
|
152
|
+
puts "xor y #{y.to_bits}"
|
153
|
+
end
|
154
|
+
|
155
|
+
y ^= (y << 7) & (0x0fe00000 & b)
|
156
|
+
|
157
|
+
if debug
|
158
|
+
puts " = #{y.to_bits}"
|
159
|
+
puts
|
160
|
+
puts "y<<7 #{((y<<7) & 0xffffffff).to_bits}"
|
161
|
+
puts "&with #{(0xf0000000 & b).to_bits}"
|
162
|
+
puts " = #{((y << 7) & (0xf0000000 & b)).to_bits}"
|
163
|
+
puts "xor y #{y.to_bits}"
|
164
|
+
end
|
165
|
+
|
166
|
+
y ^= (y << 7) & (0xf0000000 & b)
|
167
|
+
|
168
|
+
if debug
|
169
|
+
puts " = #{y.to_bits}"
|
170
|
+
puts y
|
171
|
+
end
|
172
|
+
y
|
173
|
+
end
|
174
|
+
|
175
|
+
def build_state!
|
176
|
+
_state = [seed]
|
177
|
+
for i in (1...n)
|
178
|
+
prev = _state[i - 1]
|
179
|
+
val = lowest_bits((f * (prev ^ (prev >> (w-2)) ) + i))
|
180
|
+
_state << val
|
181
|
+
end
|
182
|
+
_state
|
183
|
+
end
|
184
|
+
|
185
|
+
def twist!
|
186
|
+
for i in (0...n)
|
187
|
+
cur = state[i]
|
188
|
+
x = (cur & upper_mask) + (state[(i+1) % n] & lower_mask)
|
189
|
+
xA = x >> 1
|
190
|
+
if (x % 2) != 0
|
191
|
+
xA = xA ^ a
|
192
|
+
end
|
193
|
+
state[i] = state[(i + m) % n] ^ xA
|
194
|
+
end
|
195
|
+
@index = 0
|
196
|
+
nil
|
197
|
+
end
|
198
|
+
|
199
|
+
def lower_mask
|
200
|
+
@lower_mask ||= (1 << r) - 1
|
201
|
+
end
|
202
|
+
|
203
|
+
def upper_mask
|
204
|
+
@upper_mask ||= lowest_bits(~lower_mask)
|
205
|
+
end
|
206
|
+
|
207
|
+
def set_vars!(parameters)
|
208
|
+
parameters.each do |k, v|
|
209
|
+
instance_variable_set("@#{k}", v)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def lowest_bits(num)
|
214
|
+
num & 0xffffffff
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#encoding: ASCII-8BIT
|
2
|
+
module CryptoToolchain
|
3
|
+
module Utilities
|
4
|
+
class SHA1
|
5
|
+
class << self
|
6
|
+
def hexdigest(str, state: INITIAL_STATE, append_length: 0 )
|
7
|
+
CryptoToolchain::Utilities::SHA1.new(str).hexdigest(state: state, append_length: append_length)
|
8
|
+
end
|
9
|
+
|
10
|
+
def bindigest(str, state: INITIAL_STATE, append_length: 0)
|
11
|
+
CryptoToolchain::Utilities::SHA1.new(str).bindigest(state: state, append_length: append_length)
|
12
|
+
end
|
13
|
+
alias_method :digest, :bindigest
|
14
|
+
|
15
|
+
def padding(str)
|
16
|
+
num_null_pad = (56 - (str.bytesize + 1) ) % 64
|
17
|
+
0x80.chr + (0.chr * num_null_pad) + [str.bytesize * 8].pack("Q>")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(message)
|
22
|
+
@original = message
|
23
|
+
end
|
24
|
+
|
25
|
+
def hexdigest(state: INITIAL_STATE, append_length: 0)
|
26
|
+
bindigest(state: state, append_length: append_length).unpack("H*").join
|
27
|
+
end
|
28
|
+
|
29
|
+
def bindigest(state: INITIAL_STATE, append_length: 0)
|
30
|
+
h = registers_from(state).dup
|
31
|
+
|
32
|
+
length = original.bytesize + append_length
|
33
|
+
|
34
|
+
# while (string.size % 64) != 56
|
35
|
+
num_null_pad = (56 - (length + 1) ) % 64
|
36
|
+
padding = 0x80.chr + (0.chr * num_null_pad) + [length * 8].pack("Q>")
|
37
|
+
|
38
|
+
(original + padding).in_blocks(64).each do |_block|
|
39
|
+
w = _block.unpack("L>16")
|
40
|
+
(16..79).each do |i|
|
41
|
+
w[i] = (w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]).lrot(1)
|
42
|
+
end
|
43
|
+
|
44
|
+
a, b, c, d, e = h
|
45
|
+
|
46
|
+
(0..79).each do |i|
|
47
|
+
func, k = f_and_k_for(i)
|
48
|
+
f = func.call(b, c, d)
|
49
|
+
temp = (a.lrot(5) + f + e + k + w[i]) & 0xffffffff
|
50
|
+
e = d
|
51
|
+
d = c
|
52
|
+
c = b.lrot(30)
|
53
|
+
b = a
|
54
|
+
a = temp
|
55
|
+
end
|
56
|
+
|
57
|
+
[a, b, c, d, e].each_with_index do |val, i|
|
58
|
+
h[i] = (h[i] + val) & 0xffffffff
|
59
|
+
end
|
60
|
+
end
|
61
|
+
h.pack("L>5")
|
62
|
+
end
|
63
|
+
alias_method :digest, :bindigest
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
attr_reader :original
|
68
|
+
|
69
|
+
F_FUNCTIONS = [
|
70
|
+
->(b,c,d) { (b & c) | ((~b) & d) },
|
71
|
+
->(b,c,d) { b ^ c ^ d },
|
72
|
+
->(b,c,d) { (b & c) | (b & d) | (c & d) },
|
73
|
+
->(b,c,d) { b ^ c ^ d },
|
74
|
+
].freeze
|
75
|
+
|
76
|
+
K_CONSTANTS = [ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 ].freeze
|
77
|
+
|
78
|
+
CONSTANTS = F_FUNCTIONS.zip(K_CONSTANTS).freeze
|
79
|
+
|
80
|
+
# Equivalent to [ 0x67452301, 0xefcdaB89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ] when using registers
|
81
|
+
INITIAL_STATE = "67452301efcdab8998badcfe10325476c3d2e1f0".freeze
|
82
|
+
|
83
|
+
def registers_from(hex_str)
|
84
|
+
raise ArgumentError.new("Argument must be a hex string") unless hex_str.hex?
|
85
|
+
raise ArgumentError.new("Argument must be 40 characters long") unless hex_str.length == 40
|
86
|
+
hex_str.from_hex.unpack("L>*")
|
87
|
+
end
|
88
|
+
|
89
|
+
def f_and_k_for(i)
|
90
|
+
raise ArgumentError.new("i must be in 0..79") unless i >=0 && i <= 79
|
91
|
+
CONSTANTS[i/20]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "pry-byebug"
|
5
|
+
require "pp"
|
6
|
+
require "uri"
|
7
|
+
require 'json'
|
8
|
+
require 'securerandom'
|
9
|
+
require "bigdecimal"
|
10
|
+
require "crypto_toolchain/version"
|
11
|
+
require "crypto_toolchain/extensions"
|
12
|
+
require "crypto_toolchain/utilities"
|
13
|
+
require "crypto_toolchain/tools"
|
14
|
+
require "crypto_toolchain/black_boxes"
|
15
|
+
require "crypto_toolchain/diffie_hellman"
|
16
|
+
require "crypto_toolchain/srp"
|
17
|
+
|
18
|
+
module CryptoToolchain
|
19
|
+
AES_BLOCK_SIZE = 16
|
20
|
+
PRINTABLE_CHARACTERS = ((0x20..0x7e).to_a + [0x0a, 0x0d]).map(&:chr).freeze
|
21
|
+
NIST_P = 0xffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff
|
22
|
+
NIST_G = 2
|
23
|
+
ASN1 = {
|
24
|
+
md5: "0 0\f\x06\b*\x86H\x86\xF7\r\x02\x05\x05\x00\x04\x10",
|
25
|
+
sha1: "0!0\t\x06\x05+\x0E\x03\x02\x1A\x05\x00\x04\x14",
|
26
|
+
sha256: "010\r\x06\t`\x86H\x01e\x03\x04\x02\x01\x05\x00\x04 ",
|
27
|
+
sha384: "0A0\r\x06\t`\x86H\x01e\x03\x04\x02\x02\x05\x00\x040",
|
28
|
+
sha512: "0Q0\r\x06\t`\x86H\x01e\x03\x04\x02\x03\x05\x00\x04@"
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
DSA_P = 0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1
|
32
|
+
DSA_Q = 0xf4f47f05794b256174bba6e9b396a7707e563c5b
|
33
|
+
DSA_G = 0x5958c9d3898b224b12672c0b98e06c60df923cb8bc999d119458fef538b8fa4046c8db53039db620c094c9fa077ef389b5322a559946a71903f990f1f7e0e025e2d7f7cf494aff1a0470f5b64c36b625a097f1651fe775323556fe00b3608c887892878480e99041be601a62166ca6894bdd41a7054ec89f756ba9fc95302291
|
34
|
+
end
|