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

Add interfaces for data structures #13

Merged
merged 11 commits into from
May 30, 2019
Next Next commit
Create LoginResponseInterface
  • Loading branch information
Firehed committed May 27, 2019
commit c3d7b3a79a1dccc25ada53e3aa48ee066fa6cc24
1 change: 0 additions & 1 deletion src/ChallengeProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@

interface ChallengeProvider
{

public function getChallenge(): string;
}
5 changes: 5 additions & 0 deletions src/ClientData.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public static function fromJson(string $json)
return $ret;
}

public function getApplicationParameter(): string
{
return hash('sha256', $this->origin, true);
}

/**
* Checks the 'typ' field against the allowed types in the U2F spec (sec.
* 7.1)
Expand Down
17 changes: 17 additions & 0 deletions src/LoginResponseInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);

namespace Firehed\U2F;

interface LoginResponseInterface
{
public function getChallengeProvider(): ChallengeProvider;

public function getCounter(): int;

public function getKeyHandleBinary(): string;

public function getSignature(): string;

public function getSignedData(): string;
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should, in principle, be identical across all formats, and liable to be adjusted or removed (this should always be authData | sha256(clientData), where authData is sha256(rpID) | userPresence | counter)

}
5 changes: 5 additions & 0 deletions src/ResponseTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public function getClientData(): ClientData
return $this->clientData;
}

public function getChallengeProvider(): ChallengeProvider
{
return $this->clientData;
}

protected function setSignature(string $signature): self
{
$this->signature = $signature;
Expand Down
25 changes: 6 additions & 19 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,19 @@ public function __construct()
// @codeCoverageIgnoreEnd
}
/**
* This method authenticates a `SignResponse` against outstanding
* This method authenticates a `LoginResponseInterface` against outstanding
* registrations and their corresponding `SignRequest`s. If the response's
* signature validates and the counter hasn't done anything strange, the
* registration will be returned with an updated counter value, which *must*
* be persisted for the next authentication. If any verification component
* fails, a `SE` will be thrown.
*
* @param SignResponse $response the parsed response from the user
* @param LoginResponseInterface $response the parsed response from the user
* @return RegistrationInterface if authentication succeeds
* @throws SE if authentication fails
* @throws BadMethodCallException if a precondition is not met
*/
public function authenticate(SignResponse $response): RegistrationInterface
public function authenticate(LoginResponseInterface $response): RegistrationInterface
{
if (!$this->registrations) {
throw new BadMethodCallException(
Expand Down Expand Up @@ -117,28 +117,15 @@ public function authenticate(SignResponse $response): RegistrationInterface
// match the one in the signing request, the client signed the
// wrong thing. This could possibly be an attempt at a replay
// attack.
$this->validateChallenge($response->getClientData(), $request);
$this->validateChallenge($response->getChallengeProvider(), $request);

$pem = $registration->getPublicKeyPem();

// U2F Spec:
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html#authentication-response-message-success
$to_verify = sprintf(
'%s%s%s%s',
$request->getApplicationParameter(),
chr($response->getUserPresenceByte()),
pack('N', $response->getCounter()),
// Note: Spec says this should be from the request, but that's not
// actually available via the JS API. Because we assert the
// challenge *value* from the Client Data matches the trusted one
// from the SignRequest and that value is included in the Challenge
// Parameter, this is safe unless/until SHA-256 is broken.
$response->getClientData()->getChallengeParameter()
);
$toVerify = $response->getSignedData();

// Signature must validate against
$sig_check = openssl_verify(
$to_verify,
$toVerify,
$response->getSignature(),
$pem,
\OPENSSL_ALGO_SHA256
Expand Down
21 changes: 20 additions & 1 deletion src/SignResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use Firehed\U2F\InvalidDataException as IDE;

class SignResponse
class SignResponse implements LoginResponseInterface
{
use ResponseTrait;

Expand All @@ -17,11 +17,30 @@ public function getCounter(): int
{
return $this->counter;
}

public function getUserPresenceByte(): int
{
return $this->user_presence;
}

public function getSignedData(): string
{
// U2F Spec:
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html#authentication-response-message-success
return sprintf(
'%s%s%s%s',
$this->getClientData()->getApplicationParameter(),
chr($this->getUserPresenceByte()),
pack('N', $this->getCounter()),
// Note: Spec says this should be from the request, but that's not
// actually available via the JS API. Because we assert the
// challenge *value* from the Client Data matches the trusted one
// from the SignRequest and that value is included in the Challenge
// Parameter, this is safe unless/until SHA-256 is broken.
$this->getClientData()->getChallengeParameter()
);
}

protected function parseResponse(array $response): self
{
$this->validateKeyInArray('keyHandle', $response);
Expand Down