Skip to content
This repository has been archived by the owner on Mar 25, 2024. It is now read-only.

Commit

Permalink
Adjust relying party verification
Browse files Browse the repository at this point in the history
  • Loading branch information
Firehed committed May 30, 2019
1 parent aa7998e commit fd92116
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 17 deletions.
8 changes: 8 additions & 0 deletions src/AppIdTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ public function getApplicationParameter(): string
{
return hash('sha256', $this->appId, true);
}

/**
* @return string The raw SHA-256 hash of the Relying Party ID
*/
public function getRpIdHash(): string
{
return hash('sha256', $this->appId, true);
}
}
2 changes: 2 additions & 0 deletions src/SecurityException.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class SecurityException extends Exception
const CHALLENGE_MISMATCH = 3;
const KEY_HANDLE_UNRECOGNIZED = 4;
const NO_TRUSTED_CA = 5;
const WRONG_RELYING_PARTY = 6;

const MESSAGES = [
self::SIGNATURE_INVALID => 'Signature verification failed',
Expand All @@ -23,6 +24,7 @@ class SecurityException extends Exception
self::CHALLENGE_MISMATCH => 'Response challenge does not match request',
self::KEY_HANDLE_UNRECOGNIZED => 'Key handle has not been registered',
self::NO_TRUSTED_CA => 'The attestation certificate was not signed by any trusted Certificate Authority',
self::WRONG_RELYING_PARTY => 'Relying party invalid for this server',
];

public function __construct(int $code)
Expand Down
19 changes: 10 additions & 9 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,7 @@ public function register(RegistrationResponseInterface $response): RegistrationI
}
$this->validateChallenge($response->getChallengeProvider(), $this->registerRequest);
// Check the Application Parameter
// Note: this is a bit delicate at the moment, since different
// protocols have different rules around the handling of Relying Party
// verification. Expect this to be revised.
if (!hash_equals(
$this->registerRequest->getApplicationParameter(),
$response->getRpIdHash()
)) {
throw new SE(SE::SIGNATURE_INVALID);
}
$this->validateRelyingParty($response->getRpIdHash());

if ($this->verifyCA) {
$this->verifyAttestationCertAgainstTrustedCAs($response);
Expand Down Expand Up @@ -380,6 +372,15 @@ private function generateChallenge(): string
return toBase64Web(\random_bytes(16));
}

private function validateRelyingParty(string $rpIdHash): void
{
// Note: this is a bit delicate at the moment, since different
// protocols have different rules around the handling of Relying Party
// verification. Expect this to be revised.
if (!hash_equals($this->getRpIdHash(), $rpIdHash)) {
throw new SE(SE::WRONG_RELYING_PARTY);
}
}
/**
* Compares the Challenge value from a known source against the
* user-provided value. A mismatch will throw a SE. Future
Expand Down
17 changes: 17 additions & 0 deletions tests/AppIdTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,21 @@ public function testGetApplicationParameter()
'getApplicationParamter should return the raw SHA256 hash of the application id'
);
}

/**
* @covers ::getRpIdHash
*/
public function testGetRpIdHash()
{
$obj = new class {
use AppIdTrait;
};
$appId = 'https://u2f.example.com';
$obj->setAppId($appId);
$this->assertSame(
hash('sha256', $appId, true),
$obj->getRpIdHash(),
'getRpIdHash should return the raw SHA256 hash of the application id'
);
}
}
1 change: 0 additions & 1 deletion tests/ClientDataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
*/
class ClientDataTest extends \PHPUnit\Framework\TestCase
{

/**
* @covers ::fromJson
*/
Expand Down
19 changes: 12 additions & 7 deletions tests/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/
class ServerTest extends \PHPUnit\Framework\TestCase
{
const APP_ID = 'https://u2f.example.com';
const APP_ID = 'https://u2f.ericstern.com';