2525 server_signature /3 , client_signature /3 , client_key /2 ,
2626 client_key_xor /2 ]).
2727
28+ -compile ({nowarn_unused_function , [{hi_legacy ,4 }, {hi_legacy_round ,4 }]}).
29+
2830-type algo () :: sha | sha256 | sha512 .
2931-spec salted_password (algo (), binary (), binary (), non_neg_integer ()) -> binary ().
3032
@@ -60,15 +62,29 @@ client_key_xor(ClientProof, ClientSignature) ->
6062server_signature (Algo , ServerKey , AuthMessage ) ->
6163 sha_mac (Algo , ServerKey , AuthMessage ).
6264
65+ -ifdef (NO_PBKDF2_HMAC ).
6366hi (Algo , Password , Salt , IterationCount ) ->
67+ hi_legacy (Algo , Password , Salt , IterationCount ).
68+ -else .
69+ hi (_Algo , _Password , _Salt , 0 ) ->
70+ <<>>;
71+ hi (sha , Password , Salt , IterationCount ) ->
72+ crypto :pbkdf2_hmac (sha , Password , Salt , IterationCount , 20 );
73+ hi (sha256 , Password , Salt , IterationCount ) ->
74+ crypto :pbkdf2_hmac (sha256 , Password , Salt , IterationCount , 32 );
75+ hi (sha512 , Password , Salt , IterationCount ) ->
76+ crypto :pbkdf2_hmac (sha512 , Password , Salt , IterationCount , 64 ).
77+ -endif .
78+
79+ hi_legacy (Algo , Password , Salt , IterationCount ) ->
6480 U1 = sha_mac (Algo , Password , <<Salt /binary , 0 , 0 , 0 , 1 >>),
65- crypto :exor (U1 , hi_round (Algo , Password , U1 , IterationCount - 1 )).
81+ crypto :exor (U1 , hi_legacy_round (Algo , Password , U1 , IterationCount - 1 )).
6682
67- hi_round (Algo , Password , UPrev , 1 ) ->
83+ hi_legacy_round (Algo , Password , UPrev , 1 ) ->
6884 sha_mac (Algo , Password , UPrev );
69- hi_round (Algo , Password , UPrev , IterationCount ) ->
85+ hi_legacy_round (Algo , Password , UPrev , IterationCount ) ->
7086 U = sha_mac (Algo , Password , UPrev ),
71- crypto :exor (U , hi_round (Algo , Password , U , IterationCount - 1 )).
87+ crypto :exor (U , hi_legacy_round (Algo , Password , U , IterationCount - 1 )).
7288
7389-ifdef (USE_OLD_CRYPTO_HMAC ).
7490sha_mac (Algo , Key , Data ) ->
@@ -77,3 +93,69 @@ sha_mac(Algo, Key, Data) ->
7793sha_mac (Algo , Key , Data ) ->
7894 crypto :mac (hmac , Algo , Key , Data ).
7995-endif .
96+
97+ -ifdef (EUNIT ).
98+ -include_lib (" eunit/include/eunit.hrl" ).
99+
100+ scram_algo_test_ () -> [
101+ {<<" scram_algo_" , (atom_to_binary (Algo , latin1 ))/binary , " _test" >>,
102+ fun () ->
103+ Password = <<" pencil" >>,
104+ Salt = base64 :decode (<<" QSXCR+Q6sek8bf92" >>),
105+ Iterations = Iterations ,
106+ SaltedPassword = salted_password (Algo , Password , Salt , Iterations ),
107+ ? assertEqual (DkLen , byte_size (SaltedPassword )),
108+ ClientKey = client_key (Algo , SaltedPassword ),
109+ ? assertEqual (DkLen , byte_size (ClientKey )),
110+ StoredKey = stored_key (Algo , ClientKey ),
111+ ? assertEqual (DkLen , byte_size (StoredKey )),
112+ ServerKey = server_key (Algo , SaltedPassword ),
113+ ? assertEqual (DkLen , byte_size (ServerKey )),
114+ AuthMessage = <<" p,y,t,h,o,n,r,," >>,
115+ ServerSig = server_signature (Algo , ServerKey , AuthMessage ),
116+ ? assertEqual (DkLen , byte_size (ServerSig )),
117+ ClientSig = client_signature (Algo , StoredKey , AuthMessage ),
118+ ? assertEqual (DkLen , byte_size (ClientSig )),
119+ ? assertEqual (ServerKey , crypto :mac (hmac , Algo , SaltedPassword , <<" Server Key" >>)),
120+ ? assertEqual (StoredKey , crypto :hash (Algo , client_key (Algo , SaltedPassword )))
121+ end
122+ } || {Algo , DkLen , Iterations } <- [{sha , 20 , 4096 }, {sha256 , 32 , 4096 }, {sha512 , 64 , 10000 }]].
123+
124+ client_key_xor_test () ->
125+ Key1 = <<1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 >>,
126+ Key2 = <<10 ,9 ,8 ,7 ,6 ,5 ,4 ,3 ,2 ,1 >>,
127+ Result = client_key_xor (Key1 , Key2 ),
128+ ? assertEqual (<<11 ,11 ,11 ,3 ,3 ,3 ,3 ,11 ,11 ,11 >>, Result ),
129+ ? assertEqual (Key1 , client_key_xor (Result , Key2 )).
130+
131+ scram_zero_iterations_test () ->
132+ Password = <<" test" >>,
133+ Salt = <<" salt" >>,
134+ ? assertEqual (<<>>, salted_password (sha , Password , Salt , 0 )),
135+ ? assertEqual (<<>>, salted_password (sha256 , Password , Salt , 0 )),
136+ ? assertEqual (<<>>, salted_password (sha512 , Password , Salt , 0 )).
137+
138+ -ifndef (NO_PBKDF2_HMAC ).
139+ pbkdf2_hmac_vs_hi_legacy_test_ () -> [
140+ {timeout , 30 ,
141+ {<<" scram_hi_" , (atom_to_binary (Algo , latin1 ))/binary , " _test" >>,
142+ fun () ->
143+ lists :foreach (
144+ fun (_ ) ->
145+ Password = << <<(rand :uniform (255 ))>> || _ <- lists :seq (1 , rand :uniform (128 )) >>,
146+ Salt = << <<(rand :uniform (255 ))>> || _ <- lists :seq (1 , rand :uniform (64 )) >>,
147+ Iterations = 1 + rand :uniform (10000 ),
148+ SaltedPassword1 = hi (Algo , Password , Salt , Iterations ),
149+ SaltedPassword2 = hi_legacy (Algo , Password , Salt , Iterations ),
150+ ? assertEqual (byte_size (SaltedPassword1 ), DkLen ),
151+ ? assertEqual (SaltedPassword1 , SaltedPassword2 )
152+ end ,
153+ lists :seq (1 , 500 )
154+ )
155+ end
156+ }
157+ } || {Algo , DkLen } <- [{sha , 20 }, {sha256 , 32 }, {sha512 , 64 }]
158+ ].
159+ - endif .
160+
161+ - endif .
0 commit comments