如何使用Ruby检查SHA1用户名、密码和验证器/salt是否正确



我有一个用户名、密码和这个验证器salt。我如何与Ruby核实它是否正确?

我遵循的文档/说明如下:https://www.azerothcore.org/wiki/account

我的代码atm看起来像这样:

class Account < ApplicationRecord
self.table_name = "account"
def self.check_username_password(username, password)
account = Account.find_by(username: username)
h1 = Digest::SHA1.hexdigest(username.uppercase + password.uppercase)
h2 = Digest::SHA1.hexdigest( account.salt + ..... )
h2 == account.verifier
end
end

您链接的文档将计算验证器的算法描述为:

验证器

验证器是从salt以及用户的用户名(所有大写(及其密码(全部大写(。

要获得验证器,您需要计算:

计算h1=SHA1("用户名:密码"(,替换用户的用户名和密码已转换为大写。

计算h2=SHA1(salt||h1(,其中||是串联(。PHP中的运算符(。

注意:salt和h1都是二进制字符串,而不是十六进制字符串!

将h2视为小端序的整数(第一个字节是最不显著(。

计算(g^h2(%N.

注:g和N是参数,在魔兽世界中是固定的实施

g=7

N=0x894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7

将结果转换回以小端序排列的字节数组。

根据到示例实现的链接,这似乎是SRP6的验证器,您可以将此gem用于:https://github.com/grempe/sirp.然而,它与文档并不完全一致,我认为这可能很有趣,所以我无论如何都会努力完成它。

首先,您已经开始查找h1h2,但正如注释所说,Both salt and h1 are binary, not hexadecimal strings!。因此,您需要将hexdigest替换为digest。此外,Ruby中的大写方法是upcase,您需要在两者之间加一个冒号:

h1 = Digest::SHA1.digest("#{username.upcase}:#{password.upcase}")
h2 = Digest::SHA1.digest(account.salt + h1)

接下来,它说把h2变成一个整数,就好像它存储在little-endian中一样。请记住,整数存储为字节序列,每个字节为8位;因此32位整数是4个字节。Endianness描述第一个字节映射到数字的前8位还是最后8位。在这里,评论表明这将是最后一次。现在,SHA1生成一个20字节的散列,所以我们将使用unpack方法加上H指令(它匹配每个十六进制字节(来获得所有散列。

h2_int = h2.reverse.unpack("H*").first.to_i(16)

最后,我们对给定的常量进行一些数学运算,并将其转换回字符串。^ %构造必须是模幂运算,您可以在Ruby 2.5+中只使用Integer#pow,也可以在Ruby 2.4中使用openssl的mod_exp:

g = 7
n = 0x894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7
verifier_int = g.pow(h2_int, n)
# ruby 2.4 or below:
#
# require 'openssl'
# verifier_int = g.to_bn.mod_exp(h2_int, n).to_i
verifier = [verifier_int.to_s(16)].pack('H*').reverse

把这些放在一起:

h1 = Digest::SHA1.digest("#{username.upcase}:#{password.upcase}")
h2 = Digest::SHA1.digest(account.salt + h1)
h2_int = h2.reverse.unpack("H*").first.to_i(16)
g = 7
n = 0x894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7
verifier_int = g.pow(h2_int, n)
# ruby 2.4 or below:
#
# require 'openssl'
# verifier_int = g.to_bn.mod_exp(h2_int, n).to_i
verifier = [verifier_int.to_s(16)].pack('H*').reverse

有了这个代码,我能够验证值:

username = "testaccount"
password = "testaccount"
account.salt = "xB1Vx940(|x0FxA0xD6|x7Fx86xADO'x82':(xCCWxA0x85xE1xB2xE20x1A|3gx1C" # [0xb1569430287c0fa0d67c7f86ad4f2782273a28cc57a085e1b2e2301a7c33671c.to_s(16)].unpack('H*')

生成的验证器与预期的验证器匹配:

"xB6x95xFFxEBx8ExA76ux8FxFBx0F:xE34MtxC0?xE8xD2xF1xD1x8Cx058Px8FxCDyQ H"

轨道内

经过测试,以下代码对我有效。注意,我在这里将salt的编码转换为ascii-8bit,而不是将verifier转换为utf-8,但它不会改变结果。但是,对于最后的比较,do的编码需要相同。

class Account < ApplicationRecord
def verify(password)
h1 = Digest::SHA1.digest("#{username.upcase}:#{password.upcase}")
h2 = Digest::SHA1.digest(salt.force_encoding('ascii-8bit') + h1)
h2_int = h2.reverse.unpack("H*").first.to_i(16)
g = 7
n = 0x894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7
verifier_int = g.pow(h2_int, n)
verifier = [verifier_int.to_s(16)].pack('H*').reverse
verifier == self.verifier
end
end

最新更新