class Kafka::Sasl::Scram
Constants
- MECHANISMS
Public Class Methods
new(username:, password:, mechanism: 'sha256', logger:)
click to toggle source
# File lib/kafka/sasl/scram.rb, line 14 def initialize(username:, password:, mechanism: 'sha256', logger:) @semaphore = Mutex.new @username = username @password = password @logger = TaggedLogger.new(logger) if mechanism @mechanism = MECHANISMS.fetch(mechanism) do raise Kafka::SaslScramError, "SCRAM mechanism #{mechanism} is not supported." end end end
Public Instance Methods
authenticate!(host, encoder, decoder)
click to toggle source
# File lib/kafka/sasl/scram.rb, line 35 def authenticate!(host, encoder, decoder) @logger.debug "Authenticating #{@username} with SASL #{@mechanism}" begin @semaphore.synchronize do msg = first_message @logger.debug "Sending first client SASL SCRAM message: #{msg}" encoder.write_bytes(msg) @server_first_message = decoder.bytes @logger.debug "Received first server SASL SCRAM message: #{@server_first_message}" msg = final_message @logger.debug "Sending final client SASL SCRAM message: #{msg}" encoder.write_bytes(msg) response = parse_response(decoder.bytes) @logger.debug "Received last server SASL SCRAM message: #{response}" raise FailedScramAuthentication, response['e'] if response['e'] raise FailedScramAuthentication, "Invalid server signature" if response['v'] != server_signature end rescue EOFError => e raise FailedScramAuthentication, e.message end @logger.debug "SASL SCRAM authentication successful" end
configured?()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 31 def configured? @username && @password && @mechanism end
ident()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 27 def ident @mechanism end
Private Instance Methods
auth_message()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 98 def auth_message [first_message_bare, @server_first_message, final_message_without_proof].join(',') end
client_key()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 106 def client_key hmac(salted_password, 'Client Key') end
client_proof()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 126 def client_proof Base64.strict_encode64(xor(client_key, client_signature)) end
client_signature()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 118 def client_signature hmac(stored_key, auth_message) end
digest()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 164 def digest @digest ||= case @mechanism when 'SCRAM-SHA-256' OpenSSL::Digest::SHA256.new when 'SCRAM-SHA-512' OpenSSL::Digest::SHA512.new else raise ArgumentError, "Unknown SASL mechanism '#{@mechanism}'" end end
encoded_username()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 156 def encoded_username safe_str(@username.encode(Encoding::UTF_8)) end
final_message()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 78 def final_message "#{final_message_without_proof},p=#{client_proof}" end
final_message_without_proof()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 74 def final_message_without_proof "c=biws,r=#{rnonce}" end
first_message()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 66 def first_message "n,,#{first_message_bare}" end
first_message_bare()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 70 def first_message_bare "n=#{encoded_username},r=#{nonce}" end
h(str)
click to toggle source
# File lib/kafka/sasl/scram.rb, line 130 def h(str) digest.digest(str) end
hi(str, salt, iterations)
click to toggle source
# File lib/kafka/sasl/scram.rb, line 134 def hi(str, salt, iterations) OpenSSL::PKCS5.pbkdf2_hmac( str, salt, iterations, digest.size, digest ) end
hmac(data, key)
click to toggle source
# File lib/kafka/sasl/scram.rb, line 144 def hmac(data, key) OpenSSL::HMAC.digest(digest, data, key) end
iterations()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 94 def iterations server_data['i'].to_i end
nonce()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 160 def nonce @nonce ||= SecureRandom.urlsafe_base64(32) end
parse_response(data)
click to toggle source
# File lib/kafka/sasl/scram.rb, line 152 def parse_response(data) data.split(',').map { |s| s.split('=', 2) }.to_h end
rnonce()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 86 def rnonce server_data['r'] end
safe_str(val)
click to toggle source
# File lib/kafka/sasl/scram.rb, line 175 def safe_str(val) val.gsub('=', '=3D').gsub(',', '=2C') end
salt()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 90 def salt Base64.strict_decode64(server_data['s']) end
salted_password()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 102 def salted_password hi(@password, salt, iterations) end
server_data()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 82 def server_data parse_response(@server_first_message) end
server_key()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 114 def server_key hmac(salted_password, 'Server Key') end
server_signature()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 122 def server_signature Base64.strict_encode64(hmac(server_key, auth_message)) end
stored_key()
click to toggle source
# File lib/kafka/sasl/scram.rb, line 110 def stored_key h(client_key) end
xor(first, second)
click to toggle source
# File lib/kafka/sasl/scram.rb, line 148 def xor(first, second) first.bytes.zip(second.bytes).map { |(a, b)| (a ^ b).chr }.join('') end