@@ -165,17 +165,18 @@ private function generateResponse($challenge, $password)
165165 $ serverMessageRegexp = "#^r=(?<nonce>[ \x21- \x2B\x2D- \x7E/]+) "
166166 . ",s=(?<salt>(?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9/+]{3}=|[A-Za-z0-9/+]{2}==)?) "
167167 . ",i=(?<iteration>[0-9]*) "
168- . "(,(?<additionalAttributes>.*))? $# " ;
168+ . "(?<additionalAttr>(?:,[A-Za-z]=[^,]+)*) $# " ;
169169
170170 if (!isset ($ this ->cnonce , $ this ->gs2Header ) || !preg_match ($ serverMessageRegexp , $ challenge , $ matches )) {
171171 return false ;
172172 }
173173
174- $ additionalAttributes = $ this ->parseAdditionalAttributes ($ matches );
174+ $ additionalAttributes = $ this ->parseAdditionalAttributes ($ matches[ ' additionalAttr ' ] );
175175
176176 //forbidden by RFC 5802
177- if (isset ($ additionalAttributes ['m ' ]))
177+ if (isset ($ additionalAttributes ['m ' ])) {
178178 return false ;
179+ }
179180
180181 $ nonce = $ matches ['nonce ' ];
181182 $ salt = base64_decode ($ matches ['salt ' ]);
@@ -191,9 +192,14 @@ private function generateResponse($challenge, $password)
191192 return false ;
192193 }
193194
194- //SSDP hash
195- if (!empty ($ additionalAttributes ['d ' ])) {
196- if (!$ this ->downgradeProtection ($ additionalAttributes ['d ' ])) {
195+ if (! empty ($ additionalAttributes ['h ' ])) {
196+ if (! $ this ->downgradeProtection ($ additionalAttributes ['h ' ], "\x1f" , "\x1e" )) {
197+ return false ;
198+ }
199+ }
200+
201+ if (! empty ($ additionalAttributes ['d ' ])) {
202+ if (! $ this ->downgradeProtection ($ additionalAttributes ['d ' ], '| ' , ', ' )) {
197203 return false ;
198204 }
199205 }
@@ -215,15 +221,22 @@ private function generateResponse($challenge, $password)
215221
216222 /**
217223 * @param string $expectedDowngradeProtectionHash
224+ * @param string $groupDelimiter
225+ * @param string $delimiter
218226 * @return bool
219227 */
220- private function downgradeProtection ($ expectedDowngradeProtectionHash )
228+ private function downgradeProtection ($ expectedDowngradeProtectionHash, $ groupDelimiter , $ delimiter )
221229 {
222230 if ($ this ->options ->getDowngradeProtection () === null ) {
223231 return true ;
224232 }
225233
226- $ actualDgPHash = base64_encode (call_user_func ($ this ->hash , $ this ->generateDowngradeProtectionVerification ()));
234+ $ actualDgPHash = base64_encode (
235+ call_user_func (
236+ $ this ->hash ,
237+ $ this ->generateDowngradeProtectionVerification ($ groupDelimiter , $ delimiter )
238+ )
239+ );
227240 return $ expectedDowngradeProtectionHash === $ actualDgPHash ;
228241 }
229242
@@ -248,19 +261,26 @@ private function hi($str, $salt, $i)
248261
249262 /**
250263 * This will parse all non-fixed-position additional SCRAM attributes (the optional ones and the m-attribute)
251- * @param array $matches The array returned by our regex match, MUST contain an 'additionalAttributes' key
264+ *
265+ * @param string $additionalAttributes 'additionalAttributes' string
252266 * @return array
253267 */
254- private function parseAdditionalAttributes ($ matches )
268+ private function parseAdditionalAttributes ($ additionalAttributes )
255269 {
256- $ additionalAttributes =array ();
257- $ tail =explode (', ' , $ matches ['additionalAttributes ' ]);
258- foreach ($ tail as $ entry )
259- {
260- $ entry =explode ("= " , $ entry , 2 );
261- $ additionalAttributes [$ entry [0 ]] = $ entry [1 ];
270+ if ($ additionalAttributes == "" ) {
271+ return array ();
262272 }
263- return $ additionalAttributes ;
273+
274+ $ return = array ();
275+ $ tail = explode (', ' , $ additionalAttributes );
276+
277+ foreach ($ tail as $ entry ) {
278+ $ entry = explode ("= " , $ entry , 2 );
279+ if (count ($ entry ) > 1 ) {
280+ $ return [$ entry [0 ]] = $ entry [1 ];
281+ }
282+ }
283+ return $ return ;
264284 }
265285
266286 /**
@@ -274,21 +294,22 @@ private function parseAdditionalAttributes($matches)
274294 */
275295 public function verify ($ data )
276296 {
277- $ verifierRegexp = '#^v=((?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9/+]{3}=|[A-Za-z0-9/+]{2}==)?)(,(?<additionalAttributes>.*))?$# ' ;
297+ $ verifierRegexp = '#^v=(?<verifier>(?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9/+]{3}=|[A-Za-z0-9/+]{2}==)?) '
298+ . '(?<additionalAttr>(?:,[A-Za-z]=[^,]+)*)$# ' ;
278299
279300 $ matches = array ();
280301 if (!isset ($ this ->saltedPassword , $ this ->authMessage ) || !preg_match ($ verifierRegexp , $ data , $ matches )) {
281302 // This cannot be an outcome, you never sent the challenge's response.
282303 return false ;
283304 }
284305
285- $ additionalAttributes = $ this ->parseAdditionalAttributes ($ matches );
306+ $ additionalAttribute = $ this ->parseAdditionalAttributes ($ matches[ ' additionalAttr ' ] );
286307
287- //forbidden by RFC 5802
288- if (isset ($ additionalAttributes ['m ' ]))
308+ if (isset ($ additionalAttribute ['m ' ])) {
289309 return false ;
310+ }
290311
291- $ verifier = $ matches [1 ];
312+ $ verifier = $ matches [' verifier ' ];
292313 $ proposedServerSignature = base64_decode ($ verifier );
293314 $ serverKey = call_user_func ($ this ->hmac , $ this ->saltedPassword , "Server Key " , true );
294315 $ serverSignature = call_user_func ($ this ->hmac , $ serverKey , $ this ->authMessage , true );
0 commit comments