@@ -5,20 +5,20 @@ defmodule Mongo.Auth.SCRAM do
5
5
6
6
alias Mongo.MongoDBConnection.Utils
7
7
8
- def auth ( { username , password } , s ) do
9
- # TODO: Wrap and log error
8
+ def auth ( { username , password } , db , s ) do
10
9
11
- nonce = nonce ( )
12
- first_bare = first_bare ( username , nonce )
13
- payload = first_message ( first_bare )
14
- message = [ saslStart: 1 , mechanism: "SCRAM-SHA-1" , payload: payload ]
10
+ { mechanism , digest } = select_digest ( db , username , s )
11
+ nonce = nonce ( )
12
+ first_bare = first_bare ( username , nonce )
13
+ payload = first_message ( first_bare )
14
+ message = [ saslStart: 1 , mechanism: mechanism , payload: payload ]
15
15
16
16
result =
17
- with { :ok , % { "ok" => ok } = reply } when ok == 1 <- Utils . command ( - 2 , message , s ) ,
18
- { message , signature } = first ( reply , first_bare , username , password , nonce ) ,
19
- { :ok , % { "ok" => ok } = reply } when ok == 1 <- Utils . command ( - 3 , message , s ) ,
20
- message = second ( reply , signature ) ,
17
+ with { :ok , % { "ok" => ok } = reply } when ok == 1 <- Utils . command ( - 3 , message , s ) ,
18
+ { message , signature } = first ( reply , first_bare , username , password , nonce , digest ) ,
21
19
{ :ok , % { "ok" => ok } = reply } when ok == 1 <- Utils . command ( - 4 , message , s ) ,
20
+ message = second ( reply , signature ) ,
21
+ { :ok , % { "ok" => ok } = reply } when ok == 1 <- Utils . command ( - 5 , message , s ) ,
22
22
do: final ( reply )
23
23
24
24
case result do
@@ -31,21 +31,21 @@ defmodule Mongo.Auth.SCRAM do
31
31
end
32
32
end
33
33
34
- defp first ( % { "conversationId" => 1 , "payload" => server_payload , "done" => false } ,
35
- first_bare , username , password , client_nonce ) do
34
+ defp first ( % { "conversationId" => 1 , "payload" => server_payload , "done" => false } , first_bare , username , password , client_nonce , digest ) do
35
+
36
36
params = parse_payload ( server_payload )
37
37
server_nonce = params [ "r" ]
38
38
salt = params [ "s" ] |> Base . decode64!
39
39
iter = params [ "i" ] |> String . to_integer
40
- pass = Utils . digest_password ( username , password )
41
- salted_password = hi ( pass , salt , iter )
40
+ pass = Utils . digest_password ( username , password , digest )
41
+ salted_password = hi ( pass , salt , iter , digest )
42
42
43
43
<< ^ client_nonce :: binary ( 24 ) , _ :: binary >> = server_nonce
44
44
45
45
client_message = "c=biws,r=#{ server_nonce } "
46
46
auth_message = "#{ first_bare } ,#{ server_payload . binary } ,#{ client_message } "
47
- server_signature = generate_signature ( salted_password , auth_message )
48
- proof = generate_proof ( salted_password , auth_message )
47
+ server_signature = generate_signature ( salted_password , auth_message , digest )
48
+ proof = generate_proof ( salted_password , auth_message , digest )
49
49
client_final_message = % BSON.Binary { binary: "#{ client_message } ,#{ proof } " }
50
50
message = [ saslContinue: 1 , conversationId: 1 , payload: client_final_message ]
51
51
@@ -70,32 +70,28 @@ defmodule Mongo.Auth.SCRAM do
70
70
"n=#{ encode_username ( username ) } ,r=#{ nonce } "
71
71
end
72
72
73
- defp hi ( password , salt , iterations ) do
74
- Mongo.PBKDF2Cache . pbkdf2 ( password , salt , iterations )
73
+ defp hi ( password , salt , iterations , digest ) do
74
+ Mongo.PBKDF2Cache . pbkdf2 ( password , salt , iterations , digest )
75
75
end
76
76
77
- defp generate_proof ( salted_password , auth_message ) do
78
- client_key = :crypto . hmac ( :sha , salted_password , "Client Key" )
79
- stored_key = :crypto . hash ( :sha , client_key )
80
- signature = :crypto . hmac ( :sha , stored_key , auth_message )
77
+ defp generate_proof ( salted_password , auth_message , digest ) do
78
+ client_key = :crypto . hmac ( digest , salted_password , "Client Key" )
79
+ stored_key = :crypto . hash ( digest , client_key )
80
+ signature = :crypto . hmac ( digest , stored_key , auth_message )
81
81
client_proof = xor_keys ( client_key , signature , "" )
82
82
"p=#{ Base . encode64 ( client_proof ) } "
83
83
end
84
84
85
- defp generate_signature ( salted_password , auth_message ) do
86
- server_key = :crypto . hmac ( :sha , salted_password , "Server Key" )
87
- :crypto . hmac ( :sha , server_key , auth_message )
85
+ defp generate_signature ( salted_password , auth_message , digest ) do
86
+ server_key = :crypto . hmac ( digest , salted_password , "Server Key" )
87
+ :crypto . hmac ( digest , server_key , auth_message )
88
88
end
89
89
90
- defp xor_keys ( "" , "" , result ) ,
91
- do: result
92
- defp xor_keys ( << fa , ra :: binary >> , << fb , rb :: binary >> , result ) ,
93
- do: xor_keys ( ra , rb , << result :: binary , fa ^^^ fb >> )
94
-
90
+ defp xor_keys ( "" , "" , result ) , do: result
91
+ defp xor_keys ( << fa , ra :: binary >> , << fb , rb :: binary >> , result ) , do: xor_keys ( ra , rb , << result :: binary , fa ^^^ fb >> )
95
92
96
93
defp nonce do
97
- :crypto . strong_rand_bytes ( 18 )
98
- |> Base . encode64
94
+ :crypto . strong_rand_bytes ( 18 ) |> Base . encode64
99
95
end
100
96
101
97
defp encode_username ( username ) do
@@ -109,4 +105,22 @@ defmodule Mongo.Auth.SCRAM do
109
105
|> String . split ( "," )
110
106
|> Enum . into ( % { } , & List . to_tuple ( String . split ( & 1 , "=" , parts: 2 ) ) )
111
107
end
108
+
109
+ ##
110
+ # selects the supported sasl mechanism
111
+ # It calls isMaster with saslSupportedMechs option to ask for the selected user which mechanism is supported
112
+ #
113
+ defp select_digest ( database , username , state ) do
114
+ with { :ok , reply } <- Utils . command ( - 2 , [ isMaster: 1 , saslSupportedMechs: database <> "." <> username ] , state ) do
115
+ select_digest ( reply )
116
+ end
117
+ end
118
+ defp select_digest ( % { "saslSupportedMechs" => mechs } ) do
119
+ case Enum . member? ( mechs , "SCRAM-SHA-256" ) do
120
+ true -> { "SCRAM-SHA-256" , :sha256 }
121
+ false -> { "SCRAM-SHA-1" , :sha }
122
+ end
123
+ end
124
+ defp select_digest ( _ ) , do: { "SCRAM-SHA-1" , :sha }
125
+
112
126
end
0 commit comments