Class: Mongo::Auth::ScramConversationBase Private
Relationships & Source Files | |
Extension / Inclusion / Inheritance Descendants | |
Subclasses:
Mongo::Auth::Scram256::Conversation, Mongo::Auth::Scram::Conversation
|
|
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
|
|
Instance Chain:
|
|
Inherits: |
Mongo::Auth::SaslConversationBase
|
Defined in: | lib/mongo/auth/scram_conversation_base.rb |
Overview
Defines common behavior around authentication conversations between the client and the server.
Constant Summary
-
MIN_ITER_COUNT =
The minimum iteration count for SCRAM-SHA-1 and SCRAM-SHA-256.
4096
SaslConversationBase
- Inherited
Class Method Summary
-
.new(user, connection, client_nonce: nil) ⇒ ScramConversationBase
constructor
Internal use only
Create the new conversation.
ConversationBase
- Inherited
.new | Create the new conversation. |
Instance Attribute Summary
- #client_nonce ⇒ String readonly Internal use only
-
#id ⇒ Integer
readonly
Internal use only
Get the id of the conversation.
-
#server_verified? ⇒ true | fase
readonly
Internal use only
Whether the client verified the ServerSignature from the server.
-
#auth_message
readonly
private
Internal use only
::Mongo::Auth
message algorithm implementation. -
#iterations
readonly
private
Internal use only
Get the iterations from the server response.
-
#payload_data
readonly
private
Internal use only
Get the data from the returned payload.
-
#salt
readonly
private
Internal use only
Gets the salt from the server response.
-
#server_nonce
readonly
private
Internal use only
Get the server nonce from the payload.
ConversationBase
- Inherited
Instance Method Summary
-
#continue(reply_document, connection) ⇒ Protocol::Message
Internal use only
Continue the SCRAM conversation.
-
#finalize(connection) ⇒ Protocol::Message
Internal use only
Finalize the SCRAM conversation.
-
#process_continue_response(reply_document)
Internal use only
Processes the second response from the server.
-
#speculative_auth_document ⇒ Hash | nil
Internal use only
Returns the hash to provide to the server in the handshake as value of the speculativeAuthenticate key.
- #cache_key(*extra) private Internal use only
-
#check_server_signature(payload_data)
private
Internal use only
Looks for field ‘v’ in payload data, if it is present verifies the server signature.
-
#client_empty_message
private
Internal use only
Get the empty client message.
-
#client_final
private
Internal use only
::Mongo::Client
final implementation. -
#client_final_message
private
Internal use only
Get the final client message.
- #client_first_message_options private Internal use only
- #client_first_payload private Internal use only
-
#client_key
private
Internal use only
::Mongo::Client
key algorithm implementation. -
#client_proof(key, signature)
private
Internal use only
::Mongo::Client
proof algorithm implementation. -
#client_signature(key, message)
private
Internal use only
::Mongo::Client
signature algorithm implementation. - #compare_digest(a, b) private Internal use only
-
#first_bare
private
Internal use only
First bare implementation.
-
#h(string)
private
Internal use only
H algorithm implementation.
-
#hmac(data, key)
private
Internal use only
HMAC algorithm implementation.
-
#parse_payload(payload) ⇒ Hash
private
Internal use only
Parses a payload like a=value,b=value2 into a hash like
=> ‘value’, ‘b’ => ‘value2’
. -
#server_key
private
Internal use only
::Mongo::Server
key algorithm implementation. -
#server_signature
private
Internal use only
::Mongo::Server
signature algorithm implementation. -
#stored_key(key)
private
Internal use only
Stored key algorithm implementation.
-
#without_proof
private
Internal use only
Get the without proof message.
-
#xor(first, second)
private
Internal use only
XOR operation for two strings.
SaslConversationBase
- Inherited
#start | Start the SASL conversation. |
#auth_mechanism_name | Gets the auth mechanism name for the conversation class. |
#client_first_document, #client_first_message_options, | |
#validate_server_nonce! | Helper method to validate that server nonce starts with the client nonce. |
ConversationBase
- Inherited
#build_message, | |
#speculative_auth_document | Returns the hash to provide to the server in the handshake as value of the speculativeAuthenticate key. |
#validate_external_auth_source |
Instance Attribute Details
#auth_message (readonly, private)
::Mongo::Auth
message algorithm implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 164
attr_reader :
#client_nonce ⇒ String
(readonly)
# File 'lib/mongo/auth/scram_conversation_base.rb', line 44
attr_reader :client_nonce
#id ⇒ Integer
(readonly)
Get the id of the conversation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 52
attr_reader :id
#iterations (readonly, private)
Get the iterations from the server response.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 288
attr_reader :iterations
#payload_data (readonly, private)
Get the data from the returned payload.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 295
attr_reader :payload_data
#salt (readonly, private)
Gets the salt from the server response.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 309
attr_reader :salt
#server_nonce (readonly, private)
Get the server nonce from the payload.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 302
attr_reader :server_nonce
#server_verified? ⇒ true
| fase
(readonly)
Whether the client verified the ServerSignature from the server.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 59
def server_verified? !!@server_verified end
Instance Method Details
#cache_key(*extra) (private)
# File 'lib/mongo/auth/scram_conversation_base.rb', line 312
def cache_key(*extra) [user.password, salt, iterations, @mechanism] + extra end
#check_server_signature(payload_data) (private)
Looks for field ‘v’ in payload data, if it is present verifies the server signature. If verification succeeds, sets @server_verified to true. If verification fails, raises InvalidSignature.
This method can be called from different conversation steps depending on whether the short SCRAM conversation is used.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 205
def check_server_signature(payload_data) if verifier = payload_data['v'] if compare_digest(verifier, server_signature) @server_verified = true else raise Error::InvalidSignature.new(verifier, server_signature) end end end
#client_empty_message (private)
Get the empty client message.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 171
def BSON::Binary.new('') end
#client_final (private)
::Mongo::Client
final implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 193
def client_final @client_final ||= client_proof(client_key, client_signature(stored_key(client_key), )) end
#client_final_message (private)
Get the final client message.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 182
def BSON::Binary.new("#{without_proof},p=#{client_final}") end
#client_first_message_options (private)
# File 'lib/mongo/auth/scram_conversation_base.rb', line 148
def {skipEmptyExchange: true} end
#client_first_payload (private)
# File 'lib/mongo/auth/scram_conversation_base.rb', line 153
def client_first_payload "n,,#{}" end
#client_key (private)
::Mongo::Client
key algorithm implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 222
def client_key @client_key ||= CredentialCache.cache(cache_key(:client_key)) do hmac(salted_password, 'Client Key') end end
#client_proof(key, signature) (private)
::Mongo::Client
proof algorithm implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 235
def client_proof(key, signature) @client_proof ||= Base64.strict_encode64(xor(key, signature)) end
#client_signature(key, message) (private)
::Mongo::Client
signature algorithm implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 246
def client_signature(key, ) @client_signature ||= hmac(key, ) end
#compare_digest(a, b) (private)
# File 'lib/mongo/auth/scram_conversation_base.rb', line 371
def compare_digest(a, b) check = a.bytesize ^ b.bytesize a.bytes.zip(b.bytes){ |x, y| check |= x ^ y.to_i } check == 0 end
#continue(reply_document, connection) ⇒ Protocol::Message
Continue the SCRAM conversation. This sends the client final message to the server after setting the reply from the previous server communication.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 73
def continue(reply_document, connection) @id = reply_document['conversationId'] payload_data = reply_document['payload'].data parsed_data = parse_payload(payload_data) @server_nonce = parsed_data.fetch('r') @salt = Base64.strict_decode64(parsed_data.fetch('s')) @iterations = parsed_data.fetch('i').to_i.tap do |i| if i < MIN_ITER_COUNT raise Error::InsufficientIterationCount.new( Error::InsufficientIterationCount. (MIN_ITER_COUNT, i)) end end @auth_message = "#{},#{payload_data},#{without_proof}" validate_server_nonce! selector = CLIENT_CONTINUE_MESSAGE.merge( payload: , conversationId: id, ) (connection, user.auth_source, selector) end
#finalize(connection) ⇒ Protocol::Message
Finalize the SCRAM conversation. This is meant to be iterated until the provided reply indicates the conversation is finished.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 111
def finalize(connection) selector = CLIENT_CONTINUE_MESSAGE.merge( payload: , conversationId: id, ) (connection, user.auth_source, selector) end
#first_bare (private)
First bare implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 257
def @first_bare ||= "n=#{user.encoded_name},r=#{client_nonce}" end
#h(string) (private)
H algorithm implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 268
def h(string) digest.digest(string) end
#hmac(data, key) (private)
HMAC algorithm implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 279
def hmac(data, key) OpenSSL::HMAC.digest(digest, data, key) end
#parse_payload(payload) ⇒ Hash
(private)
Parses a payload like a=value,b=value2 into a hash like => ‘value’, ‘b’ => ‘value2’
.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 138
def parse_payload(payload) Hash[payload.split(',').reject { |v| v == '' }.map do |pair| k, v, = pair.split('=', 2) if k == '' raise Error::InvalidServerAuthResponse, 'Payload malformed: missing key' end [k, v] end] end
#process_continue_response(reply_document)
Processes the second response from the server.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 100
def process_continue_response(reply_document) payload_data = parse_payload(reply_document['payload'].data) check_server_signature(payload_data) end
#server_key (private)
::Mongo::Server
key algorithm implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 323
def server_key @server_key ||= CredentialCache.cache(cache_key(:server_key)) do hmac(salted_password, 'Server Key') end end
#server_signature (private)
::Mongo::Server
signature algorithm implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 336
def server_signature @server_signature ||= Base64.strict_encode64(hmac(server_key, )) end
#speculative_auth_document ⇒ Hash
| nil
Returns the hash to provide to the server in the handshake as value of the speculativeAuthenticate key.
If the auth mechanism does not support speculative authentication, this method returns nil.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 126
def speculative_auth_document client_first_document.merge(db: user.auth_source) end
#stored_key(key) (private)
Stored key algorithm implementation.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 347
def stored_key(key) h(key) end
#without_proof (private)
Get the without proof message.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 358
def without_proof @without_proof ||= "c=biws,r=#{server_nonce}" end
#xor(first, second) (private)
XOR operation for two strings.
# File 'lib/mongo/auth/scram_conversation_base.rb', line 367
def xor(first, second) first.bytes.zip(second.bytes).map{ |(a,b)| (a ^ b).chr }.join('') end