1- /* Copyright 2018–present MongoDB Inc.
1+ /* Copyright 2018–present MongoDB Inc.
22*
33* Licensed under the Apache License, Version 2.0 (the "License");
44* you may not use this file except in compliance with the License.
@@ -72,8 +72,8 @@ protected ScramShaAuthenticator(UsernamePasswordCredential credential,
7272 H h ,
7373 Hi hi ,
7474 Hmac hmac )
75- : this ( credential , hashAlgorithmName , new DefaultRandomStringGenerator ( ) , h , hi , hmac ) { }
76-
75+ : this ( credential , hashAlgorithmName , new DefaultRandomStringGenerator ( ) , h , hi , hmac , new ScramCache ( ) ) { }
76+
7777 /// <summary>
7878 /// Initializes a new instance of the <see cref="ScramShaAuthenticator"/> class.
7979 /// </summary>
@@ -83,14 +83,16 @@ protected ScramShaAuthenticator(UsernamePasswordCredential credential,
8383 /// <param name="h">The H function to use.</param>
8484 /// <param name="hi">The Hi function to use.</param>
8585 /// <param name="hmac">The Hmac function to use.</param>
86+ /// <param name="cache">The cache to use.</param>
8687 internal ScramShaAuthenticator (
8788 UsernamePasswordCredential credential ,
8889 HashAlgorithmName hashAlgorithName ,
8990 IRandomStringGenerator randomStringGenerator ,
9091 H h ,
9192 Hi hi ,
92- Hmac hmac )
93- : base ( new ScramShaMechanism ( credential , hashAlgorithName , randomStringGenerator , h , hi , hmac ) )
93+ Hmac hmac ,
94+ ScramCache cache )
95+ : base ( new ScramShaMechanism ( credential , hashAlgorithName , randomStringGenerator , h , hi , hmac , cache ) )
9496 {
9597 _databaseName = credential . Source ;
9698 }
@@ -108,14 +110,16 @@ private class ScramShaMechanism : ISaslMechanism
108110 private readonly Hi _hi ;
109111 private readonly Hmac _hmac ;
110112 private readonly string _name ;
113+ private ScramCache _cache ;
111114
112115 public ScramShaMechanism (
113116 UsernamePasswordCredential credential ,
114117 HashAlgorithmName hashAlgorithmName ,
115118 IRandomStringGenerator randomStringGenerator ,
116119 H h ,
117120 Hi hi ,
118- Hmac hmac )
121+ Hmac hmac ,
122+ ScramCache cache )
119123 {
120124 _credential = Ensure . IsNotNull ( credential , nameof ( credential ) ) ;
121125 _h = h ;
@@ -127,6 +131,7 @@ public ScramShaMechanism(
127131 }
128132 _name = $ "SCRAM-SHA-{ hashAlgorithmName . ToString ( ) . Substring ( 3 ) } ";
129133 _randomStringGenerator = Ensure . IsNotNull ( randomStringGenerator , nameof ( randomStringGenerator ) ) ;
134+ _cache = cache ;
130135 }
131136
132137 public string Name => _name ;
@@ -143,9 +148,9 @@ public ISaslStep Initialize(IConnection connection, SaslConversation conversatio
143148
144149 var clientFirstMessageBare = username + "," + nonce ;
145150 var clientFirstMessage = gs2Header + clientFirstMessageBare ;
146- var clientFirstMessageBytes = Utf8Encodings . Strict . GetBytes ( clientFirstMessage ) ;
151+ var clientFirstMessageBytes = Utf8Encodings . Strict . GetBytes ( clientFirstMessage ) ;
147152
148- return new ClientFirst ( clientFirstMessageBytes , clientFirstMessageBare , _credential , r , _h , _hi , _hmac ) ;
153+ return new ClientFirst ( clientFirstMessageBytes , clientFirstMessageBare , _credential , r , _h , _hi , _hmac , _cache ) ;
149154 }
150155
151156 private string GenerateRandomString ( )
@@ -163,7 +168,7 @@ private string PrepUsername(string username)
163168
164169 private class ClientFirst : ISaslStep
165170 {
166-
171+
167172 private readonly byte [ ] _bytesToSendToServer ;
168173 private readonly string _clientFirstMessageBare ;
169174 private readonly UsernamePasswordCredential _credential ;
@@ -173,14 +178,17 @@ private class ClientFirst : ISaslStep
173178 private readonly Hi _hi ;
174179 private readonly Hmac _hmac ;
175180
181+ private ScramCache _cache ;
182+
176183 public ClientFirst (
177184 byte [ ] bytesToSendToServer ,
178185 string clientFirstMessageBare ,
179186 UsernamePasswordCredential credential ,
180187 string rPrefix ,
181188 H h ,
182189 Hi hi ,
183- Hmac hmac )
190+ Hmac hmac ,
191+ ScramCache cache )
184192 {
185193 _bytesToSendToServer = bytesToSendToServer ;
186194 _clientFirstMessageBare = clientFirstMessageBare ;
@@ -189,6 +197,7 @@ public ClientFirst(
189197 _hi = hi ;
190198 _hmac = hmac ;
191199 _rPrefix = rPrefix ;
200+ _cache = cache ;
192201 }
193202
194203 public byte [ ] BytesToSendToServer => _bytesToSendToServer ;
@@ -214,19 +223,31 @@ public ISaslStep Transition(SaslConversation conversation, byte[] bytesReceivedF
214223 var nonce = "r=" + r ;
215224 var clientFinalMessageWithoutProof = channelBinding + "," + nonce ;
216225
217- var saltedPassword = _hi (
218- _credential ,
219- Convert . FromBase64String ( s ) ,
220- int . Parse ( i ) ) ;
226+ var salt = Convert . FromBase64String ( map [ 's' ] ) ;
227+ var iterations = int . Parse ( map [ 'i' ] ) ;
228+
229+ byte [ ] clientKey ;
230+ byte [ ] serverKey ;
231+
232+ var cacheKey = new ScramCacheKey ( _credential . SaslPreppedPassword , salt , iterations ) ;
233+ if ( _cache . TryGet ( cacheKey , out var cacheEntry ) )
234+ {
235+ clientKey = cacheEntry . ClientKey ;
236+ serverKey = cacheEntry . ServerKey ;
237+ }
238+ else
239+ {
240+ var saltedPassword = _hi ( _credential , salt , iterations ) ;
241+ clientKey = _hmac ( encoding , saltedPassword , "Client Key" ) ;
242+ serverKey = _hmac ( encoding , saltedPassword , "Server Key" ) ;
243+ _cache . Add ( cacheKey , new ScramCacheEntry ( clientKey , serverKey ) ) ;
244+ }
221245
222- var clientKey = _hmac ( encoding , saltedPassword , "Client Key" ) ;
223246 var storedKey = _h ( clientKey ) ;
224247 var authMessage = _clientFirstMessageBare + "," + serverFirstMessage + "," + clientFinalMessageWithoutProof ;
225248 var clientSignature = _hmac ( encoding , storedKey , authMessage ) ;
226249 var clientProof = XOR ( clientKey , clientSignature ) ;
227- var serverKey = _hmac ( encoding , saltedPassword , "Server Key" ) ;
228250 var serverSignature = _hmac ( encoding , serverKey , authMessage ) ;
229-
230251 var proof = "p=" + Convert . ToBase64String ( clientProof ) ;
231252 var clientFinalMessage = clientFinalMessageWithoutProof + "," + proof ;
232253
@@ -243,7 +264,6 @@ private byte[] XOR(byte[] a, byte[] b)
243264
244265 return result ;
245266 }
246-
247267 }
248268
249269 private class ClientLast : ISaslStep
@@ -287,4 +307,4 @@ private bool ConstantTimeEquals(byte[] a, byte[] b)
287307 }
288308 }
289309 }
290- }
310+ }
0 commit comments