# Buckhill/U2F

## Introduction

`Buckhill/U2F` is PHP library for registering and authenticating using [U2F protocol](https://www.yubico.com/about/background/fido/).

-----

## Protocol overview

U2F is a challenge-response protocol. Server presents a challenge which the device processes and returns the output.
For U2F to work, server needs to have a list of keys associated with a person (client). 

Process of exchanging U2F key metadata is called **enrollment**.
After the key is enrolled with the server, we can issue **authentication challenge** and process **authentication response**.

## Key concepts

`U2F` works in conjunction with client's browser (only Chrome supports U2F at the time of writing this document).
`U2F` works **only** over `TLS` / `SSL`. 
That means you need to have an `SSL` certificate for the web application which wants to use `U2F` tokens.
`U2F` devices operate using something called `appId`, which in practical terms, is the name of your domain (it's a string value).
If your website is located at `https://www.example.com` then the `appId` string is `https://www.example.com`. 
When you issue enrollment or authentication challenge, you must supply `appId` in the HTTP request.
Not issuing `appId` with the challenge will fail the `U2F` procedure.

-----

TL;DR: 
- works only over SSL
- must include the URL of your domain with challenges 

## Enrolling

-----
#### Issuing a challenge

Important note: when you issue a challenge, you should save what you issued to database/nosql.
When response arrives, you need to pull the challenge information and compare it with the result in response.
Details about this are covered in next section.

```php
<?php

use function Buckhill\U2F\u2f;

// Assuming you are using HTTP POST method, fetch the appId from $_POST

$app_id = $_POST['appId']; // https://www.example.com

// Returns an associative array
$challenge = u2f()->enroll()->challenge($app_id);

// The array from above has the following structure:
/*
Array
(
    [appId] => https://your.website.tld
    [challenge] => FF3fgz4TPHrJltw2D0/J98kMjRz6Qhq8
    [version] => U2F_V2
)
*/

// You can deliver the array from above as JSON, so it can be used by JavaScript U2F extension
// Note: use JSON_UNESCAPED_SLASHES while encoding, othwerise Yubikey U2F will not understand the challenge
echo json_encode($challenge, JSON_UNESCAPED_SLASHES);
```

#### Parsing enrollment response 

Assuming you saved the challenge information to the database and that you know how to retrieve it, the code for finalizing enrollment is:

```php
<?php

use function Buckhill\U2F\u2f;

$data = [
    'registrationData'  => $_POST['registrationData'],
    'clientData'        => $_POST['clientData'],
    'challenge'         => $_POST['challenge'],
    'appId'             => $_POST['appId'],
    'version'           => $_POST['version']
];

try
{
    /** @var $response \Buckhill\U2F\Enroll\Response */
    $response = u2f()->enroll()->response($data);

    // Now you can save the result to the database, since $response can be retrieved as an array
    $result = $response->toArray();

    /*
    Array
    (
        [pubKey] => BKXUTVNz3bi+xcBcKgJZPJPP28sJszAbB2dJiSfcpt8mnFWW2ZN+TBSZwtb+YCZyQJbLInEnxeiyueASVCLd/Rk=
        [keyHandle] => 4xv32Z6BJj479D-jJrOja-SQkWkWBp3BGu8uEAX2pBsXAg-6XFI_fpshtMv33wcFL6qeXSKFZmLQih2JrnajVA
        [certificate] =>  
        [appId] => https://server.mfa.web
        [counter] => 0
    )
    */
}
catch(\Exception $e)
{
	// This is example of error reporting. Do not use this approach in live environment.
	echo "Uh, oh, somethign went wrong";
	echo "Error reported: ". $e->getMessage();
}

```
-----

## Authenticating

Authenticating is challenge-response based as well and consists of two steps - issuing a challenge, which will be signed by U2F device and checking the response using key information that's saved on server.
Authentication challenge should be saved to the server as well.


#### Authentication challenge

```php
<?php

use Buckhill\U2F\U2FKey;
use function Buckhill\U2F\u2f;

// This is what you should load from the database
$key = U2FKey::fromArray([
	'keyHandle'     => 'TBl7BtPWkiiB7GEdphPferDvTdnB1wSdjSYtHYg3BqNh19RKSNgv6-hPROB-pqeoq3rlel5NsnFqeLUAhF5Nbg',
	'appId'         => 'https://www.example.com',
	'version'       => U2F::U2F_VERSION,
	'counter'       => 0
]);

$auth = u2f()->authentication->challenge($key);

echo $auth->toJSON();
```


#### Validating authentication response

```php
<?php

use Buckhill\U2F\U2FKey;
use Illuminate\Http\Request;
use function Buckhill\U2F\u2f;

// In this step, you must know what the challenge was. That's why we save auth/enroll challenges to the database, so we can reference them later while checking responses
$key = U2FKey::fromArray([
	// Challenge string is loaded from the database with authentication challenges that were issued
	'challenge'     => 'YnJHbzRiNXdQYW5YNnBhbUozOHBJaVpkZ0Jrb3pzSVU',
	'keyHandle'     => 'TBl7BtPWkiiB7GEdphPferDvTdnB1wSdjSYtHYg3BqNh19RKSNgv6-hPROB-pqeoq3rlel5NsnFqeLUAhF5Nbg',
	'appId'         => 'https://server.mfa.web',
	'version'       => U2F::U2F_VERSION,
	'counter'       => 0
]);

$request_data = [
	'clientData'	=> $_POST['clientData'],
	'keyHandle'		=> $_POST['keyHandle'],
	'signatureData'	=> $_POST['signatureData']
];

// Note for Laravel/Lumen users: you can use Illuminate\Http\Request object and pass parameters using $request->all()
// so you can avoid constructing above array manually

try
{
	/** @var $result Buckhill\U2F\U2FKey */
	$result = u2f()->authentication()->response($request_data, $key);
	
	// Upon success, you should save the new counter value to your database to avoid potential replay attacks
	$counter = $result->getCounter(); // Returns an integer
	
	echo json_encode(['message' => 'success!']);
}
catch(\Exception $e)
{
	// Do not use this kind of error reporting in production. Log the error on the server and return a meaningful message back to the client
	echo "An error occurred: " . $e->getMessage();
}

```