diff --git a/src/Authentication/AbstractAuthentication.php b/src/Authentication/AbstractAuthentication.php index b343100..eb4430d 100644 --- a/src/Authentication/AbstractAuthentication.php +++ b/src/Authentication/AbstractAuthentication.php @@ -119,9 +119,9 @@ protected function generateDowngradeProtectionVerification() usort($allowedMechanisms, array($this, 'sortOctetCollation')); usort($allowedChannelBindings, array($this, 'sortOctetCollation')); - $protect = implode(',', $allowedMechanisms); + $protect = implode("\x1e", $allowedMechanisms); if (count($allowedChannelBindings) > 0) { - $protect .= '|' . implode(',', $allowedChannelBindings); + $protect .= "\x1f" . implode("\x1e", $allowedChannelBindings); } return $protect; } diff --git a/src/Authentication/SCRAM.php b/src/Authentication/SCRAM.php index bf6c030..1c9d905 100644 --- a/src/Authentication/SCRAM.php +++ b/src/Authentication/SCRAM.php @@ -165,13 +165,18 @@ private function generateResponse($challenge, $password) $serverMessageRegexp = "#^r=(?[\x21-\x2B\x2D-\x7E/]+)" . ",s=(?(?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9/+]{3}=|[A-Za-z0-9/+]{2}==)?)" . ",i=(?[0-9]*)" - . "(?:,d=(?(?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9/+]{3}=|[A-Za-z0-9/+]{2}==)))?" - . "(,[A-Za-z]=[^,])*$#"; + . "(,(?.*))?$#"; if (!isset($this->cnonce, $this->gs2Header) || !preg_match($serverMessageRegexp, $challenge, $matches)) { return false; } + $additionalAttributes = $this->parseAdditionalAttributes($matches); + + //forbidden by RFC 5802 + if(isset($additionalAttributes['m'])) + return false; + $nonce = $matches['nonce']; $salt = base64_decode($matches['salt']); if (!$salt) { @@ -186,8 +191,9 @@ private function generateResponse($challenge, $password) return false; } - if (!empty($matches['downgradeProtection'])) { - if (!$this->downgradeProtection($matches['downgradeProtection'])) { + //SSDP hash + if (!empty($additionalAttributes['h'])) { + if (!$this->downgradeProtection($additionalAttributes['h'])) { return false; } } @@ -240,6 +246,23 @@ private function hi($str, $salt, $i) return $result; } + /** + * This will parse all non-fixed-position additional SCRAM attributes (the optional ones and the m-attribute) + * @param array $matches The array returned by our regex match, MUST contain an 'additionalAttributes' key + * @return array + */ + private function parseAdditionalAttributes($matches) + { + $additionalAttributes=array(); + $tail=explode(',', $matches['additionalAttributes']); + foreach($tail as $entry) + { + $entry=explode("=", $entry, 2); + $additionalAttributes[$entry[0]] = $entry[1]; + } + return $additionalAttributes; + } + /** * SCRAM has also a server verification step. On a successful outcome, it will send additional data which must * absolutely be checked against this function. If this fails, the entity which we are communicating with is @@ -251,7 +274,7 @@ private function hi($str, $salt, $i) */ public function verify($data) { - $verifierRegexp = '#^v=((?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9/+]{3}=|[A-Za-z0-9/+]{2}==)?)$#'; + $verifierRegexp = '#^v=((?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9/+]{3}=|[A-Za-z0-9/+]{2}==)?)(,(?.*))?$#'; $matches = array(); if (!isset($this->saltedPassword, $this->authMessage) || !preg_match($verifierRegexp, $data, $matches)) { @@ -259,6 +282,12 @@ public function verify($data) return false; } + $additionalAttributes = $this->parseAdditionalAttributes($matches); + + //forbidden by RFC 5802 + if(isset($additionalAttributes['m'])) + return false; + $verifier = $matches[1]; $proposedServerSignature = base64_decode($verifier); $serverKey = call_user_func($this->hmac, $this->saltedPassword, "Server Key", true);