Skip to content

Commit

Permalink
Apply WinRb#53
Browse files Browse the repository at this point in the history
  • Loading branch information
adfoster-r7 committed Jul 20, 2022
1 parent 72163c5 commit 1fe8c6f
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 31 deletions.
8 changes: 5 additions & 3 deletions lib/net/ntlm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
require 'net/ntlm/message/type3'

require 'net/ntlm/encode_util'
require 'net/ntlm/md4'
require 'net/ntlm/rc4'

require 'net/ntlm/client'
require 'net/ntlm/channel_binding'
Expand Down Expand Up @@ -124,9 +126,9 @@ def gen_keys(str)

def apply_des(plain, keys)
keys.map {|k|
dec = OpenSSL::Cipher.new("des-cbc").encrypt
dec = OpenSSL::Cipher.new("des-ede-cbc").encrypt
dec.padding = 0
dec.key = k
dec.key = k + k
dec.update(plain) + dec.final
}
end
Expand All @@ -146,7 +148,7 @@ def ntlm_hash(password, opt = {})
unless opt[:unicode]
pwd = EncodeUtil.encode_utf16le(pwd)
end
OpenSSL::Digest::MD4.digest pwd
Net::NTLM::Md4.digest pwd
end

# Generate a NTLMv2 Hash
Expand Down
37 changes: 9 additions & 28 deletions lib/net/ntlm/client/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,8 @@ def authenticate!
t3 = Message::Type3.create type3_opts
if negotiate_key_exchange?
t3.enable(:session_key)
rc4 = OpenSSL::Cipher.new("rc4")
rc4.encrypt
rc4.key = user_session_key
sk = rc4.update exported_session_key
sk << rc4.final
rc4 = Net::NTLM::Rc4.new(user_session_key)
sk = rc4.encrypt exported_session_key
t3.session_key = sk
end
t3
Expand All @@ -50,7 +47,7 @@ def exported_session_key
@exported_session_key ||=
begin
if negotiate_key_exchange?
OpenSSL::Cipher.new("rc4").random_key
OpenSSL::Random.random_bytes(16)
else
user_session_key
end
Expand All @@ -61,8 +58,7 @@ def sign_message(message)
seq = sequence
sig = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, client_sign_key, "#{seq}#{message}")[0..7]
if negotiate_key_exchange?
sig = client_cipher.update sig
sig << client_cipher.final
sig = client_cipher.encrypt sig
end
"#{VERSION_MAGIC}#{sig}#{seq}"
end
Expand All @@ -71,20 +67,17 @@ def verify_signature(signature, message)
seq = signature[-4..-1]
sig = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, server_sign_key, "#{seq}#{message}")[0..7]
if negotiate_key_exchange?
sig = server_cipher.update sig
sig << server_cipher.final
sig = server_cipher.encrypt sig
end
"#{VERSION_MAGIC}#{sig}#{seq}" == signature
end

def seal_message(message)
emessage = client_cipher.update(message)
emessage + client_cipher.final
client_cipher.encrypt(message)
end

def unseal_message(emessage)
message = server_cipher.update(emessage)
message + server_cipher.final
server_cipher.encrypt(emessage)
end

private
Expand Down Expand Up @@ -123,23 +116,11 @@ def server_seal_key
end

def client_cipher
@client_cipher ||=
begin
rc4 = OpenSSL::Cipher.new("rc4")
rc4.encrypt
rc4.key = client_seal_key
rc4
end
@client_cipher ||= Net::NTLM::Rc4.new(client_seal_key)
end

def server_cipher
@server_cipher ||=
begin
rc4 = OpenSSL::Cipher.new("rc4")
rc4.decrypt
rc4.key = server_seal_key
rc4
end
@server_cipher ||= Net::NTLM::Rc4.new(server_seal_key)
end

def client_challenge
Expand Down
80 changes: 80 additions & 0 deletions lib/net/ntlm/md4.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
require 'openssl'

module Net
module NTLM

class Md4

begin
OpenSSL::Digest::MD4.digest("")
rescue
# libssl-3.0+ doesn't support legacy MD4 -> use our own implementation

require 'stringio'

def self.digest(string)
# functions
mask = (1 << 32) - 1
f = proc {|x, y, z| x & y | x.^(mask) & z}
g = proc {|x, y, z| x & y | x & z | y & z}
h = proc {|x, y, z| x ^ y ^ z}
r = proc {|v, s| (v << s).&(mask) | (v.&(mask) >> (32 - s))}

# initial hash
a, b, c, d = 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476

bit_len = string.size << 3
string += "\x80"
while (string.size % 64) != 56
string += "\0"
end
string = string.force_encoding('ascii-8bit') + [bit_len & mask, bit_len >> 32].pack("V2")

if string.size % 64 != 0
fail "failed to pad to correct length"
end

io = StringIO.new(string)
block = ""

while io.read(64, block)
x = block.unpack("V16")

# Process this block.
aa, bb, cc, dd = a, b, c, d
[0, 4, 8, 12].each {|i|
a = r[a + f[b, c, d] + x[i], 3]; i += 1
d = r[d + f[a, b, c] + x[i], 7]; i += 1
c = r[c + f[d, a, b] + x[i], 11]; i += 1
b = r[b + f[c, d, a] + x[i], 19]
}
[0, 1, 2, 3].each {|i|
a = r[a + g[b, c, d] + x[i] + 0x5a827999, 3]; i += 4
d = r[d + g[a, b, c] + x[i] + 0x5a827999, 5]; i += 4
c = r[c + g[d, a, b] + x[i] + 0x5a827999, 9]; i += 4
b = r[b + g[c, d, a] + x[i] + 0x5a827999, 13]
}
[0, 2, 1, 3].each {|i|
a = r[a + h[b, c, d] + x[i] + 0x6ed9eba1, 3]; i += 8
d = r[d + h[a, b, c] + x[i] + 0x6ed9eba1, 9]; i -= 4
c = r[c + h[d, a, b] + x[i] + 0x6ed9eba1, 11]; i += 8
b = r[b + h[c, d, a] + x[i] + 0x6ed9eba1, 15]
}
a = (a + aa) & mask
b = (b + bb) & mask
c = (c + cc) & mask
d = (d + dd) & mask
end

[a, b, c, d].pack("V4")
end

else
# Openssl/libssl provides MD4, so we can use it.
def self.digest(string)
OpenSSL::Digest::MD4.digest(string)
end
end
end
end
end
59 changes: 59 additions & 0 deletions lib/net/ntlm/rc4.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
require 'openssl'

module Net
module NTLM

begin
OpenSSL::Cipher.new("rc4")
rescue
# libssl-3.0+ doesn't support legacy Rc4 -> use our own implementation

class Rc4
def initialize(str)
raise ArgumentError, "RC4: Key supplied is blank" if str.eql?('')
initialize_state(str)
@q1, @q2 = 0, 0
end

def encrypt(text)
text.each_byte.map do |b|
@q1 = (@q1 + 1) % 256
@q2 = (@q2 + @state[@q1]) % 256
@state[@q1], @state[@q2] = @state[@q2], @state[@q1]
b ^ @state[(@state[@q1] + @state[@q2]) % 256]
end.pack("C*")
end

private

# The initial state which is then modified by the key-scheduling algorithm
INITIAL_STATE = (0..255).to_a

# Performs the key-scheduling algorithm to initialize the state.
def initialize_state(key)
i = j = 0
@state = INITIAL_STATE.dup
key_length = key.length
while i < 256
j = (j + @state[i] + key.getbyte(i % key_length)) % 256
@state[i], @state[j] = @state[j], @state[i]
i += 1
end
end
end

else
# Openssl/libssl provides RC4, so we can use it.
class Rc4
def initialize(str)
@ci = OpenSSL::Cipher.new("rc4")
@ci.key = str
end

def encrypt(text)
@ci.update(text) + @ci.final
end
end
end
end
end

0 comments on commit 1fe8c6f

Please sign in to comment.