class Net::IMAP::SASL::ScramAuthenticator
“SCRAM-*
”系列 SASL
机制的抽象基类,定义在 RFC5802 中。通过 Net::IMAP#authenticate
使用。
直接支持
-
SCRAM-SHA-1
—ScramSHA1Authenticator
-
SCRAM-SHA-256
—ScramSHA256Authenticator
对于 OpenSSL::Digest 支持的任何哈希算法,都可以轻松添加新的 SCRAM-*
机制。子类只需要设置一个适当的 DIGEST_NAME
常量。
SCRAM 算法¶ ↑
请参阅 ScramAlgorithm
上的文档和方法定义,了解该算法的概述。不同的机制仅在使用哪个哈希函数方面有所不同(或通过对具有 -PLUS
的通道绑定的支持而有所不同)。
另请参阅 GS2Header
上的方法。
服务器消息¶ ↑
收到服务器消息后,它们将被验证并加载到各种属性中,例如:snonce
、salt
、iterations
、verifier、server_error
等。
与许多其他 SASL
机制不同,SCRAM-*
系列支持相互身份验证,并且可以在服务器消息中返回服务器错误数据。如果 process
为服务器最终消息引发 Error
,则 server_error
可能包含错误详细信息。
TLS 通道绑定¶ ↑
目前不支持 SCRAM-*-PLUS
机制和通道绑定。
缓存 SCRAM 密钥¶ ↑
目前不支持缓存 salted_password、client_key、stored_key 和 server_key。
属性
授权身份:代表或充当的身份。身份形式是特定于应用程序协议的。如果未提供或留空,服务器将从身份验证身份派生授权身份。例如,管理员或超级用户可能会承担另一个角色。
imap.authenticate "SCRAM-SHA-256", "root", passwd, authzid: "user"
服务器负责验证客户端的凭据,并验证与其客户端身份验证身份关联的身份是否允许充当(或代表)授权身份。
由 SecureRandom 生成的客户端随机数
所选哈希函数和用户的迭代计数
允许的最小迭代计数。较低的 iterations
将引发 Error
。
与 username
匹配的密码或密码短语。
服务器为此用户使用的盐
与 username
匹配的密码或密码短语。
SASL 交换期间服务器报告的错误。
不包括协议报告的错误,例如 Net::IMAP::NoResponseError
。
需要存储此项以用于 auth_message
服务器随机数,必须以 cnonce
开头
公共类方法
为“SCRAM-*
”SASL
机制之一创建身份验证器。每个子类都定义 digest
以匹配特定的机制。
由 Net::IMAP#authenticate
以及其他客户端上的类似方法调用。
参数¶ ↑
-
可选
authzid
— 代表或充当的备用身份。 -
可选
min_iterations
- 覆盖默认值 (4096)。
任何其他关键字参数都会被忽略。
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 80 def initialize(username_arg = nil, password_arg = nil, authcid: nil, username: nil, authzid: nil, password: nil, secret: nil, min_iterations: 4096, # see both RFC5802 and RFC7677 cnonce: nil, # must only be set in tests **options) @username = username || username_arg || authcid or raise ArgumentError, "missing username (authcid)" @password = password || secret || password_arg or raise ArgumentError, "missing password" @authzid = authzid @min_iterations = Integer min_iterations @min_iterations.positive? or raise ArgumentError, "min_iterations must be positive" @cnonce = cnonce || SecureRandom.base64(32) end
公共实例方法
返回一个新的 OpenSSL::Digest 对象,设置为所选机制的适当哈希函数。
该类的 DIGEST_NAME
常量必须设置为 OpenSSL::Digest 支持的算法的名称。
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 155 def digest; OpenSSL::Digest.new self.class::DIGEST_NAME end
身份验证交换是否完成?
如果为 false,则需要另一个服务器继续。
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 185 def done?; @state == :done end
请参阅 RFC5802 §7 client-first-message
。
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 159 def initial_client_response "#{gs2_header}#{client_first_message_bare}" end
响应服务器的质询
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 164 def process(challenge) case (@state ||= :initial_client_response) when :initial_client_response initial_client_response.tap { @state = :server_first_message } when :server_first_message recv_server_first_message challenge final_message_with_proof.tap { @state = :server_final_message } when :server_final_message recv_server_final_message challenge "".tap { @state = :done } else raise Error, "server sent after complete, %p" % [challenge] end rescue Exception => ex @state = ex raise end
私有实例方法
请参阅 RFC5802 §7 client-final-message-without-proof
。
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 240 def client_final_message_without_proof @client_final_message_without_proof ||= format_message(c: [cbind_input].pack("m0"), # channel-binding r: snonce) # nonce end
请参阅 RFC5802 §7 client-first-message-bare
。
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 225 def client_first_message_bare @client_first_message_bare ||= format_message(n: gs2_saslname_encode(SASL.saslprep(username)), r: cnonce) end
请参阅 RFC5802 §7 client-final-message
。
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 233 def final_message_with_proof proof = [client_proof].pack("m0") "#{client_final_message_without_proof},p=#{proof}" end
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 192 def format_message(hash) hash.map { _1.join("=") }.join(",") end
RFC5802 指定“客户端或服务器消息中的属性顺序是固定的,但扩展属性除外”,但此代码仅将其解析为哈希,而无需考虑顺序。请注意,重复的键(违反规范)将使用最后一个值。
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 257 def parse_challenge(challenge) challenge.split(/,/).to_h {|pair| pair.split(/=/, 2) } rescue ArgumentError raise Error, "unparsable challenge: %p" % [challenge] end
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 211 def recv_server_final_message(server_final_message) sparams = parse_challenge server_final_message @server_error = sparams["e"] and raise Error, "server error: %s" % [server_error] verifier = sparams["v"].unpack1("m") or raise Error, "server did not send verifier" verifier == server_signature or raise Error, "server verify failed: %p != %p" % [ server_signature, verifier ] end
# File net-imap-0.5.4/lib/net/imap/sasl/scram_authenticator.rb, line 194 def recv_server_first_message(server_first_message) @server_first_message = server_first_message sparams = parse_challenge server_first_message @snonce = sparams["r"] or raise Error, "server did not send nonce" @salt = sparams["s"]&.unpack1("m") or raise Error, "server did not send salt" @iterations = sparams["i"]&.then {|i| Integer i } or raise Error, "server did not send iteration count" min_iterations <= iterations or raise Error, "too few iterations: %d" % [iterations] mext = sparams["m"] and raise Error, "mandatory extension: %p" % [mext] snonce.start_with? cnonce or raise Error, "invalid server nonce" end