browserid: Move to successor project portier (#52)

This commit is contained in:
onli 2017-02-22 18:33:36 +01:00 committed by GitHub
parent ef1eaefc61
commit 873db4afa9
343 changed files with 44998 additions and 134 deletions

View file

@ -0,0 +1,19 @@
Copyright (C) 2016 Angry Bytes
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,67 @@
# portier-php
A [Portier] client library for PHP
[Portier]: https://portier.github.io/
### Example
```php
<?php
require 'vendor/autoload.php';
$app = new \Slim\App();
$redis = new Redis();
$redis->pconnect('127.0.0.1', 6379);
$portier = new \Portier\Client\Client(
new \Portier\Client\RedisStore($redis),
'http://localhost:8000/verify'
);
$app->get('/', function($req, $res) {
$res = $res
->withStatus(200)
->withHeader('Content-Type', 'text/html; charset=utf-8');
$res->getBody()->write(
<<<EOF
<p>Enter your email address:</p>
<form method="post" action="/auth">
<input name="email" type="email">
<button type="submit">Login</button>
</form>
EOF
);
return $res;
});
$app->post('/auth', function($req, $res) use ($portier) {
$authUrl = $portier->authenticate($req->getParsedBodyParam('email'));
return $res
->withStatus(303)
->withHeader('Location', $authUrl);
});
$app->post('/verify', function($req, $res) use ($portier) {
$email = $portier->verify($req->getParsedBodyParam('id_token'));
$res = $res
->withStatus(200)
->withHeader('Content-Type', 'text/html; charset=utf-8');
$res->getBody()->write(
<<<EOF
<p>Verified email address ${email}!</p>
EOF
);
return $res;
});
$app->run();
```

View file

@ -0,0 +1,73 @@
<?php
namespace Portier\Client;
/**
* A store implementation that uses Redis as the backend.
*/
class S9yStore extends AbstractStore
{
public $plugin;
/**
* Constructor
*/
public function __construct($plugin)
{
parent::__construct();
$this->plugin = $plugin;
}
/**
* {@inheritDoc}
*/
public function fetchCached($cacheId, $url)
{
$key = 'cache:' . $cacheId;
$data = $this->get_config_ttl($key);
if ($data) {
return json_decode($data);
}
$res = $this->fetch($url);
$this->set_config_ttl($key, json_encode($res->data), $res->ttl);
return $res->data;
}
/**
* {@inheritDoc}
*/
public function createNonce($email)
{
$nonce = $this->generateNonce($email);
$this->set_config_ttl("nonce_$email", $nonce, $this->nonceTtl);
return $nonce;
}
/**
* {@inheritDoc}
*/
public function consumeNonce($nonce, $email)
{
$storedNonce = $this->get_config_ttl("nonce_$email");
$this->plugin->set_config("nonce_$email", null);
if ($storedNonce === null || $storedNonce !== $nonce) {
throw new \Exception('Invalid or expired nonce');
}
}
private function set_config_ttl($key, $value, $ttl) {
$this->plugin->set_config($key, $value);
$this->plugin->set_config("$key_valid_till", time() + $ttl);
}
private function get_config_ttl($key) {
$value = $this->plugin->get_config($key);
if (time() < $this->plugin->get_config("$key_valid_till")) {
return $value;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,27 @@
{
"name": "portier/client",
"description": "Portier client for PHP",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Stéphan Kochen",
"email": "stephan@kochen.nl"
}
],
"autoload": {
"psr-4": {
"Portier\\Client\\": "src/"
}
},
"require": {
"spomky-labs/base64url": "^1.0",
"fgrosse/phpasn1": "^1.5",
"lcobucci/jwt": "^3.2",
"guzzlehttp/guzzle": "^6.2"
},
"require-dev": {
"phpunit/phpunit": "^5.6",
"squizlabs/php_codesniffer": "^2.7"
}
}

1805
serendipity_event_browserid/composer.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite>
<directory>tests/</directory>
</testsuite>
</testsuites>
</phpunit>

View file

@ -16,18 +16,16 @@ class serendipity_event_browserid extends serendipity_event
$propbag->add('name', PLUGIN_BROWSERID_NAME);
$propbag->add('description', PLUGIN_BROWSERID_DESC);
$propbag->add('stackable', false);
$propbag->add('author', 'Grischa Brockhaus');
$propbag->add('version', '1.1');
$propbag->add('author', 'Grischa Brockhaus, Malte Paskuda');
$propbag->add('version', '2.0');
$propbag->add('requirements', array(
'serendipity' => '1.6',
'smarty' => '2.6.7',
'php' => '5.1.3'
'serendipity' => '2.0',
'php' => '7.0'
));
$propbag->add('groups', array('BACKEND_USERMANAGEMENT'));
$propbag->add('event_hooks', array(
'backend_login' => true,
'backend_login_page' => true,
'backend_header' => true,
'external_plugin' => true,
));
@ -55,27 +53,26 @@ class serendipity_event_browserid extends serendipity_event
function event_hook($event, &$bag, &$eventData, $addData = null) {
global $serendipity;
static $login_url = null;
if ($login_url === null) {
$login_url = $serendipity['baseURL'] . $serendipity['indexFile'] . '?/plugin/loginbox';
}
require __DIR__ . '/vendor/autoload.php';
require_once 'S9yStore.php';
$verify_url = $serendipity['baseURL'] . 'index.php?/plugin/serendipity_event_browserid_verify';
$this->portier = new \Portier\Client\Client(
new \Portier\Client\S9yStore($this),
$verify_url
);
$hooks = &$bag->get('event_hooks');
if (isset($hooks[$event])) {
switch($event) {
case 'external_plugin':
if ($eventData=="serendipity_event_browserid.js") {
header('Content-Type: text/javascript');
echo file_get_contents(dirname(__FILE__). '/serendipity_event_browserid.js');
}
else if ($eventData=="browserid_signin.png") {
header('Content-Type: image/png');
echo file_get_contents(dirname(__FILE__). '/browserid_signin.png');
if ($eventData=="serendipity_event_browserid_auth") {
$this->auth($serendipity['POST']['persona_email']);
}
else if ($eventData=="serendipity_event_browserid_verify") {
$this->verify();
$this->verify($_POST['id_token']);
}
break;
@ -92,9 +89,6 @@ class serendipity_event_browserid extends serendipity_event
}
return;
case 'backend_header':
$this->print_backend_header();
return true;
default:
return false;
@ -104,131 +98,79 @@ class serendipity_event_browserid extends serendipity_event
}
}
function verify() {
function verify($idToken) {
global $serendipity;
$url = 'https://browserid.org/verify';
$assert = $_POST['assert'];
$params = 'assertion='.$assert.'&audience=' .
urlencode($serendipity['baseURL']);
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_POST,2);
curl_setopt($ch,CURLOPT_POSTFIELDS, $params);
$result = curl_exec($ch);
curl_close($ch);
$response = json_decode($result);
if (isset($response) && $response->status=='okay') {
$email = $response->email;
$audience = $response->audience;
if ($audience!=$serendipity['baseURL']) { // The login has the wrong host!
$response->status = 'errorhost';
$response->message= "Internal error logging you in (wrong host: $audience)";
$_SESSION['serendipityAuthedUser'] = false;
@session_destroy();
}
else { // host ist correct, check what we have with this email
$password = md5($email);
$query = "SELECT DISTINCT a.email, a.authorid, a.userlevel, a.right_publish, a.realname
FROM
{$serendipity['dbPrefix']}authors AS a
WHERE
a.email = '{$email}'";
$row = serendipity_db_query($query, true, 'assoc');
if (is_array($row)) {
serendipity_setCookie('old_session', session_id());
serendipity_setAuthorToken();
$_SESSION['serendipityUser'] = $serendipity['serendipityUser'] = $row['realname'];
$_SESSION['serendipityPassword'] = $serendipity['serendipityPassword'] = $password;
$_SESSION['serendipityEmail'] = $serendipity['serendipityEmail'] = $email;
$_SESSION['serendipityAuthorid'] = $serendipity['authorid'] = $row['authorid'];
$_SESSION['serendipityUserlevel'] = $serendipity['serendipityUserlevel'] = $row['userlevel'];
$_SESSION['serendipityAuthedUser'] = $serendipity['serendipityAuthedUser'] = true;
$_SESSION['serendipityRightPublish']= $serendipity['serendipityRightPublish'] = $row['right_publish'];
// Prevent session manupulation:
$_SESSION['serendipityBrowserID'] = $this->get_install_token();
serendipity_load_configuration($serendipity['authorid']);
}
else { // No user found for that email!
$response->status = 's9yunknown';
$response->message= "Sorry, we don't have a user for $email";
$_SESSION['serendipityAuthedUser'] = false;
@session_destroy();
}
}
$result = json_encode($response);
}
echo $result;
$email = $this->portier->verify($idToken);
$this->login_user($email);
header("Location: ${_SESSION['serendipity_event_browserid_loginurl']}", true, 303);
}
function auth($email) {
global $serendipity;
$authUrl = $this->portier->authenticate($email);
header("Location: $authUrl", true, 303);
}
function login_user($email) {
global $serendipity;
$query = "SELECT DISTINCT a.email, a.authorid, a.userlevel, a.right_publish, a.realname
FROM
{$serendipity['dbPrefix']}authors AS a
WHERE
a.email = '{$email}'";
$row = serendipity_db_query($query, true, 'assoc');
if (is_array($row)) {
serendipity_setCookie('old_session', session_id());
serendipity_setAuthorToken();
$_SESSION['serendipityUser'] = $serendipity['serendipityUser'] = $row['realname'];
$_SESSION['serendipityPassword'] = $serendipity['serendipityPassword'] = serendipity_hash($email);
$_SESSION['serendipityEmail'] = $serendipity['serendipityEmail'] = $email;
$_SESSION['serendipityAuthorid'] = $serendipity['authorid'] = $row['authorid'];
$_SESSION['serendipityUserlevel'] = $serendipity['serendipityUserlevel'] = $row['userlevel'];
$_SESSION['serendipityAuthedUser'] = $serendipity['serendipityAuthedUser'] = true;
$_SESSION['serendipityRightPublish']= $serendipity['serendipityRightPublish'] = $row['right_publish'];
serendipity_load_configuration($serendipity['authorid']);
} else { // No user found for that email!
echo "found no such user";
$response->status = 's9yunknown';
$response->message= "Sorry, we don't have a user for $email";
$_SESSION['serendipityAuthedUser'] = false;
@session_destroy();
}
}
function reauth() {
global $serendipity;
// Reauth only, if valid session
if (isset($_SESSION['serendipityBrowserID']) && $_SESSION['serendipityBrowserID']===$this->get_install_token()) {
$serendipity['serendipityUser'] = $_SESSION['serendipityUser'];
$serendipity['serendipityPassword'] = $_SESSION['serendipityPassword'];
$serendipity['serendipityEmail'] = $_SESSION['serendipityEmail'];
$serendipity['authorid'] = $_SESSION['serendipityAuthorid'];
$serendipity['serendipityUserlevel'] = $_SESSION['serendipityUserlevel'];
$serendipity['serendipityAuthedUser'] = $_SESSION['serendipityAuthedUser'];
$serendipity['serendipityRightPublish'] = $_SESSION['serendipityRightPublish'];
serendipity_load_configuration($serendipity['authorid']);
return true;
}
return false;
}
/**
* Produces or loads a token unique to this installation.
*/
function get_install_token() {
$token = $this->get_config("installationtoken");
if (empty($token)) {
$token = md5(time());
$this->set_config("installationtoken", $token);
global $serendipity;
// Reauth only, if valid session
if ($_SESSION['serendipityAuthedUser']) {
$serendipity['serendipityUser'] = $_SESSION['serendipityUser'];
$serendipity['serendipityPassword'] = $_SESSION['serendipityPassword'];
$serendipity['serendipityEmail'] = $_SESSION['serendipityEmail'];
$serendipity['authorid'] = $_SESSION['serendipityAuthorid'];
$serendipity['serendipityUserlevel'] = $_SESSION['serendipityUserlevel'];
$serendipity['serendipityAuthedUser'] = $_SESSION['serendipityAuthedUser'];
$serendipity['serendipityRightPublish'] = $_SESSION['serendipityRightPublish'];
serendipity_load_configuration($serendipity['authorid']);
return true;
}
return $token;
}
function print_backend_header() {
echo '
<script src="https://browserid.org/include.js" type="text/javascript"></script>
';
return false;
}
function print_loginpage(&$eventData) {
global $serendipity;
$hidden = array('action'=>'admin');
$bid_title = "Sign-in with BrowserID";
$local_signin_img = $serendipity['baseURL'] . 'index.php?/plugin/browserid_signin.png';
$local_js = $serendipity['baseURL'] . 'index.php?/plugin/serendipity_event_browserid.js';
$verify_url = $serendipity['baseURL'] . 'index.php?/plugin/serendipity_event_browserid_verify';
$eventData['header'] .= '
<!-- browserid start -->
<script type="text/javascript">var browserid_verify="'. $verify_url . '";</script>
<div align="center">
<section><button><img src="' . $local_signin_img . '" alt="' . $bid_title . '" title="' . $bid_title . '"></button></section>
</div>
<script src="' . $local_js . '" type="text/javascript"></script>
<!-- browserid end -->
';
$_SESSION['serendipity_event_browserid_loginurl'] = $_SERVER['REDIRECT_SCRIPT_URI'] . '?' . $_SERVER['QUERY_STRING'];
$auth_url = $serendipity['baseURL'] . 'index.php?/plugin/serendipity_event_browserid_auth';
echo '<form method="post" action="' . $auth_url . '" style="margin: auto; max-width: 23em; border: 1px solid #aaa; margin-top: 4em; padding: 1em;">
<fieldset>
<span class="wrap_legend"><legend>Please enter your email</legend></span>
<input name="serendipity[persona_email]" type="email">
<button type="submit">Login</button>
</fieldset>
</form>';
}
function print_backend_footer() {
global $serendipity;
$local_js = $serendipity['baseURL'] . 'index.php?/plugin/serendipity_event_browserid.js';
echo '
<!-- browserid start -->
<script src="' . $local_js . '" type="text/javascript"></script>
<!-- browserid end -->
';
}
}
/* vim: set sts=4 ts=4 expandtab : */

View file

@ -0,0 +1,82 @@
<?php
namespace Portier\Client;
/**
* An abstract base class for stores.
*
* Offers default implementations for fetching and generating nonces.
*/
abstract class AbstractStore implements StoreInterface
{
/**
* The Guzzle instance to use.
* @var \GuzzleHttp\Client
*/
public $guzzle;
/**
* Lifespan of a nonce.
* @var float
*/
public $nonceTtl = 15 * 60;
/**
* Minimum time to cache a HTTP response
* @var float
*/
public $cacheMinTtl = 60 * 60;
/**
* Constructor
*/
public function __construct()
{
$this->guzzle = new \GuzzleHttp\Client([
'timeout' => 10
]);
}
/**
* Generate a nonce value.
* @param string $email Optional email context
* @return string The generated nonce.
*/
public function generateNonce($email)
{
return bin2hex(random_bytes(16));
}
/**
* Fetch a URL using HTTP GET.
* @param string $url The URL to fetch.
* @return object An object with `ttl` and `data` properties.
*/
public function fetch($url)
{
$res = $this->guzzle->get($url);
$data = json_decode($res->getBody());
if (!($data instanceof \stdClass)) {
throw new \Exception('Invalid response body');
}
$ttl = 0;
if ($res->hasHeader('Cache-Control')) {
$cacheControl = $res->getHeaderLine('Cache-Control');
if (preg_match(
'/max-age\s*=\s*(\d+)/',
$cacheControl,
$matches
)) {
$ttl = intval($matches[1]);
}
}
$ttl = max($this->cacheMinTtl, $ttl);
return (object) [
'ttl' => $ttl,
'data' => $data,
];
}
}

View file

@ -0,0 +1,183 @@
<?php
namespace Portier\Client;
/**
* Client for a Portier broker.
*/
class Client
{
/**
* Default Portier broker origin.
* @var string
*/
const DEFAULT_BROKER = 'https://broker.portier.io';
private $store;
private $redirectUri;
private $clientId;
/**
* The origin of the Portier broker.
* @var string
*/
public $broker = self::DEFAULT_BROKER;
/**
* The number of seconds of clock drift to allow.
* @var int
*/
public $leeway = 3 * 60;
/**
* Constructor
* @param Store $store Store implementation to use.
* @param string $redirectUri URL that Portier will redirect to.
*/
public function __construct($store, $redirectUri)
{
$this->store = $store;
$this->redirectUri = $redirectUri;
$this->clientId = self::getOrigin($this->redirectUri);
}
/**
* Start authentication of an email address.
* @param string $email Email address to authenticate.
* @return string URL to redirect the browser to.
*/
public function authenticate($email)
{
$nonce = $this->store->createNonce($email);
$query = http_build_query([
'login_hint' => $email,
'scope' => 'openid email',
'nonce' => $nonce,
'response_type' => 'id_token',
'response_mode' => 'form_post',
'client_id' => $this->clientId,
'redirect_uri' => $this->redirectUri,
]);
return $this->broker . '/auth?' . $query;
}
/**
* Verify a token received on our `redirect_uri`.
* @param string $token The received `id_token` parameter value.
* @return string The verified email address.
*/
public function verify($token)
{
// Parse token and get the key ID from its header.
$parser = new \Lcobucci\JWT\Parser();
$token = $parser->parse($token);
$kid = $token->getHeader('kid');
// Fetch broker keys.
$discoveryUrl = $this->broker . '/.well-known/openid-configuration';
$discoveryDoc = $this->store->fetchCached('discovery', $discoveryUrl);
if (!isset($discoveryDoc->jwks_uri) || !is_string($discoveryDoc->jwks_uri)) {
throw new \Exception('Discovery document incorrectly formatted');
}
$keysDoc = $this->store->fetchCached('keys', $discoveryDoc->jwks_uri);
if (!isset($keysDoc->keys) || !is_array($keysDoc->keys)) {
throw new \Exception('Keys document incorrectly formatted');
}
// Find the matching public key, and verify the signature.
$publicKey = null;
foreach ($keysDoc->keys as $key) {
if (isset($key->alg) && $key->alg === 'RS256' &&
isset($key->kid) && $key->kid === $kid &&
isset($key->n) && isset($key->e)) {
$publicKey = $key;
break;
}
}
if ($publicKey === null) {
throw new \Exception('Cannot find the public key used to sign the token');
}
if (!$token->verify(
new \Lcobucci\JWT\Signer\Rsa\Sha256(),
self::parseJwk($publicKey)
)) {
throw new \Exception('Token signature did not validate');
}
// Validate the token claims.
$vdata = new \Lcobucci\JWT\ValidationData();
$vdata->setIssuer($this->broker);
$vdata->setAudience($this->clientId);
if (!$token->validate($vdata)) {
throw new \Exception('Token claims did not validate');
}
// Get the email and consume the nonce.
$nonce = $token->getClaim('nonce');
$email = $token->getClaim('sub');
$this->store->consumeNonce($nonce, $email);
return $email;
}
/**
* Parse a JWK into an OpenSSL public key.
* @param object $jwk
* @return resource
*/
private static function parseJwk($jwk)
{
$n = gmp_init(bin2hex(\Base64Url\Base64Url::decode($jwk->n)), 16);
$e = gmp_init(bin2hex(\Base64Url\Base64Url::decode($jwk->e)), 16);
$seq = new \FG\ASN1\Universal\Sequence();
$seq->addChild(new \FG\ASN1\Universal\Integer(gmp_strval($n)));
$seq->addChild(new \FG\ASN1\Universal\Integer(gmp_strval($e)));
$pkey = new \FG\X509\PublicKey(bin2hex($seq->getBinary()));
$encoded = base64_encode($pkey->getBinary());
return new \Lcobucci\JWT\Signer\Key(
"-----BEGIN PUBLIC KEY-----\n" .
chunk_split($encoded, 64, "\n") .
"-----END PUBLIC KEY-----\n"
);
}
/**
* Get the origin for a URL
* @param string $url
* @return string
*/
private static function getOrigin($url)
{
$components = parse_url($url);
if ($components === false) {
throw new \Exception('Could not parse the redirect URI');
}
if (!isset($components['scheme'])) {
throw new \Exception('No scheme set in redirect URI');
}
$scheme = $components['scheme'];
if (!isset($components['host'])) {
throw new \Exception('No host set in redirect URI');
}
$host = $components['host'];
$res = $scheme . '://' . $host;
if (isset($components['port'])) {
$port = $components['port'];
if (($scheme === 'http' && $port !== 80) ||
($scheme === 'https' && $port !== 443)) {
$res .= ':' . $port;
}
}
return $res;
}
}

View file

@ -0,0 +1,68 @@
<?php
namespace Portier\Client;
/**
* A store implementation that uses Redis as the backend.
*/
class RedisStore extends AbstractStore
{
public $redis;
/**
* Constructor
* @param \Redis $redis The Redis instance to use.
*/
public function __construct(\Redis $redis)
{
parent::__construct();
$this->redis = $redis;
}
/**
* {@inheritDoc}
*/
public function fetchCached($cacheId, $url)
{
$key = 'cache:' . $cacheId;
$data = $this->redis->get($key);
if ($data) {
return json_decode($data);
}
$res = $this->fetch($url);
$this->redis->setEx($key, $res->ttl, json_encode($res->data));
return $res->data;
}
/**
* {@inheritDoc}
*/
public function createNonce($email)
{
$nonce = $this->generateNonce($email);
$key = 'nonce:' . $nonce;
$this->redis->setEx($key, $this->nonceTtl, $email);
return $nonce;
}
/**
* {@inheritDoc}
*/
public function consumeNonce($nonce, $email)
{
$key = 'nonce:' . $nonce;
$res = $this->redis->multi()
->get($key)
->del($key)
->exec();
if ($res[0] !== $email) {
throw new \Exception('Invalid or expired nonce');
}
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Portier\Client;
/**
* Interface for stores used by the client.
*/
interface StoreInterface
{
/**
* Fetch JSON from cache or using HTTP GET.
* @param string $cacheId The cache ID to use for this request.
* @param string $url The URL to fetch of the ID is not available.
* @return object The JSON object from the response body.
*/
public function fetchCached($cacheId, $url);
/**
* Generate and store a nonce.
* @param string $email Email address to associate with the nonce.
* @return string The generated nonce.
*/
public function createNonce($email);
/**
* Consume a nonce, and check if it's valid for the given email address.
* @param string $nonce The nonce to resolve.
* @param string $email The email address that is being verified.
*/
public function consumeNonce($nonce, $email);
}

View file

@ -0,0 +1,16 @@
<?php
namespace Tests;
class AbstractStoreTest extends \PHPUnit\Framework\TestCase
{
public function testGenerateNonce()
{
$this->markTestIncomplete();
}
public function testFetch()
{
$this->markTestIncomplete();
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace Tests;
class ClientTest extends \PHPUnit\Framework\TestCase
{
public function testAuthenticate()
{
$store = $this->prophesize(\Portier\Client\StoreInterface::class);
$store->createNonce('johndoe@example.com')
->willReturn('foobar')
->shouldBeCalled();
$client = new \Portier\Client\Client(
$store->reveal(),
'https://example.com/callback'
);
$this->assertEquals(
$client->authenticate('johndoe@example.com'),
'https://broker.portier.io/auth?' . http_build_query([
'login_hint' => 'johndoe@example.com',
'scope' => 'openid email',
'nonce' => 'foobar',
'response_type' => 'id_token',
'response_mode' => 'form_post',
'client_id' => 'https://example.com',
'redirect_uri' => 'https://example.com/callback',
])
);
}
public function testVerify()
{
$this->markTestIncomplete();
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Tests;
class RedisStoreTest extends \PHPUnit\Framework\TestCase
{
public function testFetchCached()
{
$this->markTestIncomplete();
}
public function testCreateNonce()
{
$this->markTestIncomplete();
}
public function testConsumeNonce()
{
$this->markTestIncomplete();
}
}

View file

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitbff66b1076919b6333f2c7a27bd72760::getLoader();

View file

@ -0,0 +1,441 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View file

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,170 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Base64Url\\Base64Url' => $vendorDir . '/spomky-labs/base64url/src/Base64Url.php',
'FG\\ASN1\\AbstractString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/AbstractString.php',
'FG\\ASN1\\AbstractTime' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/AbstractTime.php',
'FG\\ASN1\\Base128' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Base128.php',
'FG\\ASN1\\Composite\\AttributeTypeAndValue' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php',
'FG\\ASN1\\Composite\\RDNString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php',
'FG\\ASN1\\Composite\\RelativeDistinguishedName' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php',
'FG\\ASN1\\Construct' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Construct.php',
'FG\\ASN1\\Exception\\NotImplementedException' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php',
'FG\\ASN1\\Exception\\ParserException' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php',
'FG\\ASN1\\ExplicitlyTaggedObject' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php',
'FG\\ASN1\\Identifier' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Identifier.php',
'FG\\ASN1\\OID' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/OID.php',
'FG\\ASN1\\Object' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Object.php',
'FG\\ASN1\\Parsable' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Parsable.php',
'FG\\ASN1\\TemplateParser' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/TemplateParser.php',
'FG\\ASN1\\Universal\\BMPString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php',
'FG\\ASN1\\Universal\\BitString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php',
'FG\\ASN1\\Universal\\Boolean' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php',
'FG\\ASN1\\Universal\\CharacterString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php',
'FG\\ASN1\\Universal\\Enumerated' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php',
'FG\\ASN1\\Universal\\GeneralString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php',
'FG\\ASN1\\Universal\\GeneralizedTime' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php',
'FG\\ASN1\\Universal\\GraphicString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php',
'FG\\ASN1\\Universal\\IA5String' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php',
'FG\\ASN1\\Universal\\Integer' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php',
'FG\\ASN1\\Universal\\Null' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Null.php',
'FG\\ASN1\\Universal\\NullObject' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php',
'FG\\ASN1\\Universal\\NumericString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php',
'FG\\ASN1\\Universal\\ObjectDescriptor' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php',
'FG\\ASN1\\Universal\\ObjectIdentifier' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php',
'FG\\ASN1\\Universal\\OctetString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php',
'FG\\ASN1\\Universal\\PrintableString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php',
'FG\\ASN1\\Universal\\RelativeObjectIdentifier' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php',
'FG\\ASN1\\Universal\\Sequence' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php',
'FG\\ASN1\\Universal\\Set' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/Set.php',
'FG\\ASN1\\Universal\\T61String' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php',
'FG\\ASN1\\Universal\\UTCTime' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php',
'FG\\ASN1\\Universal\\UTF8String' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php',
'FG\\ASN1\\Universal\\UniversalString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php',
'FG\\ASN1\\Universal\\VisibleString' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php',
'FG\\ASN1\\UnknownConstructedObject' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php',
'FG\\ASN1\\UnknownObject' => $vendorDir . '/fgrosse/phpasn1/lib/ASN1/UnknownObject.php',
'FG\\X509\\AlgorithmIdentifier' => $vendorDir . '/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php',
'FG\\X509\\CSR\\Attributes' => $vendorDir . '/fgrosse/phpasn1/lib/X509/CSR/Attributes.php',
'FG\\X509\\CSR\\CSR' => $vendorDir . '/fgrosse/phpasn1/lib/X509/CSR/CSR.php',
'FG\\X509\\CertificateExtensions' => $vendorDir . '/fgrosse/phpasn1/lib/X509/CertificateExtensions.php',
'FG\\X509\\CertificateSubject' => $vendorDir . '/fgrosse/phpasn1/lib/X509/CertificateSubject.php',
'FG\\X509\\PrivateKey' => $vendorDir . '/fgrosse/phpasn1/lib/X509/PrivateKey.php',
'FG\\X509\\PublicKey' => $vendorDir . '/fgrosse/phpasn1/lib/X509/PublicKey.php',
'FG\\X509\\SAN\\DNSName' => $vendorDir . '/fgrosse/phpasn1/lib/X509/SAN/DNSName.php',
'FG\\X509\\SAN\\IPAddress' => $vendorDir . '/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php',
'FG\\X509\\SAN\\SubjectAlternativeNames' => $vendorDir . '/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php',
'GuzzleHttp\\Client' => $vendorDir . '/guzzlehttp/guzzle/src/Client.php',
'GuzzleHttp\\ClientInterface' => $vendorDir . '/guzzlehttp/guzzle/src/ClientInterface.php',
'GuzzleHttp\\Cookie\\CookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php',
'GuzzleHttp\\Cookie\\CookieJarInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php',
'GuzzleHttp\\Cookie\\FileCookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php',
'GuzzleHttp\\Cookie\\SessionCookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php',
'GuzzleHttp\\Cookie\\SetCookie' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/SetCookie.php',
'GuzzleHttp\\Exception\\BadResponseException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/BadResponseException.php',
'GuzzleHttp\\Exception\\ClientException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ClientException.php',
'GuzzleHttp\\Exception\\ConnectException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ConnectException.php',
'GuzzleHttp\\Exception\\GuzzleException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/GuzzleException.php',
'GuzzleHttp\\Exception\\RequestException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/RequestException.php',
'GuzzleHttp\\Exception\\SeekException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/SeekException.php',
'GuzzleHttp\\Exception\\ServerException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ServerException.php',
'GuzzleHttp\\Exception\\TooManyRedirectsException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php',
'GuzzleHttp\\Exception\\TransferException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/TransferException.php',
'GuzzleHttp\\HandlerStack' => $vendorDir . '/guzzlehttp/guzzle/src/HandlerStack.php',
'GuzzleHttp\\Handler\\CurlFactory' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlFactory.php',
'GuzzleHttp\\Handler\\CurlFactoryInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php',
'GuzzleHttp\\Handler\\CurlHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php',
'GuzzleHttp\\Handler\\CurlMultiHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php',
'GuzzleHttp\\Handler\\EasyHandle' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php',
'GuzzleHttp\\Handler\\MockHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/MockHandler.php',
'GuzzleHttp\\Handler\\Proxy' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/Proxy.php',
'GuzzleHttp\\Handler\\StreamHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php',
'GuzzleHttp\\MessageFormatter' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatter.php',
'GuzzleHttp\\Middleware' => $vendorDir . '/guzzlehttp/guzzle/src/Middleware.php',
'GuzzleHttp\\Pool' => $vendorDir . '/guzzlehttp/guzzle/src/Pool.php',
'GuzzleHttp\\PrepareBodyMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php',
'GuzzleHttp\\Promise\\AggregateException' => $vendorDir . '/guzzlehttp/promises/src/AggregateException.php',
'GuzzleHttp\\Promise\\CancellationException' => $vendorDir . '/guzzlehttp/promises/src/CancellationException.php',
'GuzzleHttp\\Promise\\Coroutine' => $vendorDir . '/guzzlehttp/promises/src/Coroutine.php',
'GuzzleHttp\\Promise\\EachPromise' => $vendorDir . '/guzzlehttp/promises/src/EachPromise.php',
'GuzzleHttp\\Promise\\FulfilledPromise' => $vendorDir . '/guzzlehttp/promises/src/FulfilledPromise.php',
'GuzzleHttp\\Promise\\Promise' => $vendorDir . '/guzzlehttp/promises/src/Promise.php',
'GuzzleHttp\\Promise\\PromiseInterface' => $vendorDir . '/guzzlehttp/promises/src/PromiseInterface.php',
'GuzzleHttp\\Promise\\PromisorInterface' => $vendorDir . '/guzzlehttp/promises/src/PromisorInterface.php',
'GuzzleHttp\\Promise\\RejectedPromise' => $vendorDir . '/guzzlehttp/promises/src/RejectedPromise.php',
'GuzzleHttp\\Promise\\RejectionException' => $vendorDir . '/guzzlehttp/promises/src/RejectionException.php',
'GuzzleHttp\\Promise\\TaskQueue' => $vendorDir . '/guzzlehttp/promises/src/TaskQueue.php',
'GuzzleHttp\\Promise\\TaskQueueInterface' => $vendorDir . '/guzzlehttp/promises/src/TaskQueueInterface.php',
'GuzzleHttp\\Psr7\\AppendStream' => $vendorDir . '/guzzlehttp/psr7/src/AppendStream.php',
'GuzzleHttp\\Psr7\\BufferStream' => $vendorDir . '/guzzlehttp/psr7/src/BufferStream.php',
'GuzzleHttp\\Psr7\\CachingStream' => $vendorDir . '/guzzlehttp/psr7/src/CachingStream.php',
'GuzzleHttp\\Psr7\\DroppingStream' => $vendorDir . '/guzzlehttp/psr7/src/DroppingStream.php',
'GuzzleHttp\\Psr7\\FnStream' => $vendorDir . '/guzzlehttp/psr7/src/FnStream.php',
'GuzzleHttp\\Psr7\\InflateStream' => $vendorDir . '/guzzlehttp/psr7/src/InflateStream.php',
'GuzzleHttp\\Psr7\\LazyOpenStream' => $vendorDir . '/guzzlehttp/psr7/src/LazyOpenStream.php',
'GuzzleHttp\\Psr7\\LimitStream' => $vendorDir . '/guzzlehttp/psr7/src/LimitStream.php',
'GuzzleHttp\\Psr7\\MessageTrait' => $vendorDir . '/guzzlehttp/psr7/src/MessageTrait.php',
'GuzzleHttp\\Psr7\\MultipartStream' => $vendorDir . '/guzzlehttp/psr7/src/MultipartStream.php',
'GuzzleHttp\\Psr7\\NoSeekStream' => $vendorDir . '/guzzlehttp/psr7/src/NoSeekStream.php',
'GuzzleHttp\\Psr7\\PumpStream' => $vendorDir . '/guzzlehttp/psr7/src/PumpStream.php',
'GuzzleHttp\\Psr7\\Request' => $vendorDir . '/guzzlehttp/psr7/src/Request.php',
'GuzzleHttp\\Psr7\\Response' => $vendorDir . '/guzzlehttp/psr7/src/Response.php',
'GuzzleHttp\\Psr7\\ServerRequest' => $vendorDir . '/guzzlehttp/psr7/src/ServerRequest.php',
'GuzzleHttp\\Psr7\\Stream' => $vendorDir . '/guzzlehttp/psr7/src/Stream.php',
'GuzzleHttp\\Psr7\\StreamDecoratorTrait' => $vendorDir . '/guzzlehttp/psr7/src/StreamDecoratorTrait.php',
'GuzzleHttp\\Psr7\\StreamWrapper' => $vendorDir . '/guzzlehttp/psr7/src/StreamWrapper.php',
'GuzzleHttp\\Psr7\\UploadedFile' => $vendorDir . '/guzzlehttp/psr7/src/UploadedFile.php',
'GuzzleHttp\\Psr7\\Uri' => $vendorDir . '/guzzlehttp/psr7/src/Uri.php',
'GuzzleHttp\\RedirectMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/RedirectMiddleware.php',
'GuzzleHttp\\RequestOptions' => $vendorDir . '/guzzlehttp/guzzle/src/RequestOptions.php',
'GuzzleHttp\\RetryMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/RetryMiddleware.php',
'GuzzleHttp\\TransferStats' => $vendorDir . '/guzzlehttp/guzzle/src/TransferStats.php',
'GuzzleHttp\\UriTemplate' => $vendorDir . '/guzzlehttp/guzzle/src/UriTemplate.php',
'Lcobucci\\JWT\\Builder' => $vendorDir . '/lcobucci/jwt/src/Builder.php',
'Lcobucci\\JWT\\Claim' => $vendorDir . '/lcobucci/jwt/src/Claim.php',
'Lcobucci\\JWT\\Claim\\Basic' => $vendorDir . '/lcobucci/jwt/src/Claim/Basic.php',
'Lcobucci\\JWT\\Claim\\EqualsTo' => $vendorDir . '/lcobucci/jwt/src/Claim/EqualsTo.php',
'Lcobucci\\JWT\\Claim\\Factory' => $vendorDir . '/lcobucci/jwt/src/Claim/Factory.php',
'Lcobucci\\JWT\\Claim\\GreaterOrEqualsTo' => $vendorDir . '/lcobucci/jwt/src/Claim/GreaterOrEqualsTo.php',
'Lcobucci\\JWT\\Claim\\LesserOrEqualsTo' => $vendorDir . '/lcobucci/jwt/src/Claim/LesserOrEqualsTo.php',
'Lcobucci\\JWT\\Claim\\Validatable' => $vendorDir . '/lcobucci/jwt/src/Claim/Validatable.php',
'Lcobucci\\JWT\\Parser' => $vendorDir . '/lcobucci/jwt/src/Parser.php',
'Lcobucci\\JWT\\Parsing\\Decoder' => $vendorDir . '/lcobucci/jwt/src/Parsing/Decoder.php',
'Lcobucci\\JWT\\Parsing\\Encoder' => $vendorDir . '/lcobucci/jwt/src/Parsing/Encoder.php',
'Lcobucci\\JWT\\Signature' => $vendorDir . '/lcobucci/jwt/src/Signature.php',
'Lcobucci\\JWT\\Signer' => $vendorDir . '/lcobucci/jwt/src/Signer.php',
'Lcobucci\\JWT\\Signer\\BaseSigner' => $vendorDir . '/lcobucci/jwt/src/Signer/BaseSigner.php',
'Lcobucci\\JWT\\Signer\\Ecdsa' => $vendorDir . '/lcobucci/jwt/src/Signer/Ecdsa.php',
'Lcobucci\\JWT\\Signer\\Ecdsa\\KeyParser' => $vendorDir . '/lcobucci/jwt/src/Signer/Ecdsa/KeyParser.php',
'Lcobucci\\JWT\\Signer\\Ecdsa\\Sha256' => $vendorDir . '/lcobucci/jwt/src/Signer/Ecdsa/Sha256.php',
'Lcobucci\\JWT\\Signer\\Ecdsa\\Sha384' => $vendorDir . '/lcobucci/jwt/src/Signer/Ecdsa/Sha384.php',
'Lcobucci\\JWT\\Signer\\Ecdsa\\Sha512' => $vendorDir . '/lcobucci/jwt/src/Signer/Ecdsa/Sha512.php',
'Lcobucci\\JWT\\Signer\\Hmac' => $vendorDir . '/lcobucci/jwt/src/Signer/Hmac.php',
'Lcobucci\\JWT\\Signer\\Hmac\\Sha256' => $vendorDir . '/lcobucci/jwt/src/Signer/Hmac/Sha256.php',
'Lcobucci\\JWT\\Signer\\Hmac\\Sha384' => $vendorDir . '/lcobucci/jwt/src/Signer/Hmac/Sha384.php',
'Lcobucci\\JWT\\Signer\\Hmac\\Sha512' => $vendorDir . '/lcobucci/jwt/src/Signer/Hmac/Sha512.php',
'Lcobucci\\JWT\\Signer\\Key' => $vendorDir . '/lcobucci/jwt/src/Signer/Key.php',
'Lcobucci\\JWT\\Signer\\Keychain' => $vendorDir . '/lcobucci/jwt/src/Signer/Keychain.php',
'Lcobucci\\JWT\\Signer\\Rsa' => $vendorDir . '/lcobucci/jwt/src/Signer/Rsa.php',
'Lcobucci\\JWT\\Signer\\Rsa\\Sha256' => $vendorDir . '/lcobucci/jwt/src/Signer/Rsa/Sha256.php',
'Lcobucci\\JWT\\Signer\\Rsa\\Sha384' => $vendorDir . '/lcobucci/jwt/src/Signer/Rsa/Sha384.php',
'Lcobucci\\JWT\\Signer\\Rsa\\Sha512' => $vendorDir . '/lcobucci/jwt/src/Signer/Rsa/Sha512.php',
'Lcobucci\\JWT\\Token' => $vendorDir . '/lcobucci/jwt/src/Token.php',
'Lcobucci\\JWT\\ValidationData' => $vendorDir . '/lcobucci/jwt/src/ValidationData.php',
'Portier\\Client\\AbstractStore' => $baseDir . '/src/AbstractStore.php',
'Portier\\Client\\Client' => $baseDir . '/src/Client.php',
'Portier\\Client\\RedisStore' => $baseDir . '/src/RedisStore.php',
'Portier\\Client\\StoreInterface' => $baseDir . '/src/StoreInterface.php',
'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php',
'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php',
'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php',
'Psr\\Http\\Message\\ServerRequestInterface' => $vendorDir . '/psr/http-message/src/ServerRequestInterface.php',
'Psr\\Http\\Message\\StreamInterface' => $vendorDir . '/psr/http-message/src/StreamInterface.php',
'Psr\\Http\\Message\\UploadedFileInterface' => $vendorDir . '/psr/http-message/src/UploadedFileInterface.php',
'Psr\\Http\\Message\\UriInterface' => $vendorDir . '/psr/http-message/src/UriInterface.php',
);

View file

@ -0,0 +1,12 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
);

View file

@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View file

@ -0,0 +1,17 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Portier\\Client\\' => array($baseDir . '/src'),
'Lcobucci\\JWT\\' => array($vendorDir . '/lcobucci/jwt/src'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'FG\\' => array($vendorDir . '/fgrosse/phpasn1/lib'),
'Base64Url\\' => array($vendorDir . '/spomky-labs/base64url/src'),
);

View file

@ -0,0 +1,70 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitbff66b1076919b6333f2c7a27bd72760
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitbff66b1076919b6333f2c7a27bd72760', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitbff66b1076919b6333f2c7a27bd72760', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitbff66b1076919b6333f2c7a27bd72760::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitbff66b1076919b6333f2c7a27bd72760::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequirebff66b1076919b6333f2c7a27bd72760($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequirebff66b1076919b6333f2c7a27bd72760($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

View file

@ -0,0 +1,249 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitbff66b1076919b6333f2c7a27bd72760
{
public static $files = array (
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
);
public static $prefixLengthsPsr4 = array (
'P' =>
array (
'Psr\\Http\\Message\\' => 17,
'Portier\\Client\\' => 15,
),
'L' =>
array (
'Lcobucci\\JWT\\' => 13,
),
'G' =>
array (
'GuzzleHttp\\Psr7\\' => 16,
'GuzzleHttp\\Promise\\' => 19,
'GuzzleHttp\\' => 11,
),
'F' =>
array (
'FG\\' => 3,
),
'B' =>
array (
'Base64Url\\' => 10,
),
);
public static $prefixDirsPsr4 = array (
'Psr\\Http\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-message/src',
),
'Portier\\Client\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
'Lcobucci\\JWT\\' =>
array (
0 => __DIR__ . '/..' . '/lcobucci/jwt/src',
),
'GuzzleHttp\\Psr7\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
),
'GuzzleHttp\\Promise\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
),
'GuzzleHttp\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
),
'FG\\' =>
array (
0 => __DIR__ . '/..' . '/fgrosse/phpasn1/lib',
),
'Base64Url\\' =>
array (
0 => __DIR__ . '/..' . '/spomky-labs/base64url/src',
),
);
public static $classMap = array (
'Base64Url\\Base64Url' => __DIR__ . '/..' . '/spomky-labs/base64url/src/Base64Url.php',
'FG\\ASN1\\AbstractString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/AbstractString.php',
'FG\\ASN1\\AbstractTime' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/AbstractTime.php',
'FG\\ASN1\\Base128' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Base128.php',
'FG\\ASN1\\Composite\\AttributeTypeAndValue' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php',
'FG\\ASN1\\Composite\\RDNString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php',
'FG\\ASN1\\Composite\\RelativeDistinguishedName' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php',
'FG\\ASN1\\Construct' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Construct.php',
'FG\\ASN1\\Exception\\NotImplementedException' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php',
'FG\\ASN1\\Exception\\ParserException' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php',
'FG\\ASN1\\ExplicitlyTaggedObject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php',
'FG\\ASN1\\Identifier' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Identifier.php',
'FG\\ASN1\\OID' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/OID.php',
'FG\\ASN1\\Object' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Object.php',
'FG\\ASN1\\Parsable' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Parsable.php',
'FG\\ASN1\\TemplateParser' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/TemplateParser.php',
'FG\\ASN1\\Universal\\BMPString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php',
'FG\\ASN1\\Universal\\BitString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php',
'FG\\ASN1\\Universal\\Boolean' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php',
'FG\\ASN1\\Universal\\CharacterString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php',
'FG\\ASN1\\Universal\\Enumerated' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php',
'FG\\ASN1\\Universal\\GeneralString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php',
'FG\\ASN1\\Universal\\GeneralizedTime' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php',
'FG\\ASN1\\Universal\\GraphicString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php',
'FG\\ASN1\\Universal\\IA5String' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php',
'FG\\ASN1\\Universal\\Integer' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php',
'FG\\ASN1\\Universal\\Null' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Null.php',
'FG\\ASN1\\Universal\\NullObject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php',
'FG\\ASN1\\Universal\\NumericString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php',
'FG\\ASN1\\Universal\\ObjectDescriptor' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php',
'FG\\ASN1\\Universal\\ObjectIdentifier' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php',
'FG\\ASN1\\Universal\\OctetString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php',
'FG\\ASN1\\Universal\\PrintableString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php',
'FG\\ASN1\\Universal\\RelativeObjectIdentifier' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php',
'FG\\ASN1\\Universal\\Sequence' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php',
'FG\\ASN1\\Universal\\Set' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/Set.php',
'FG\\ASN1\\Universal\\T61String' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php',
'FG\\ASN1\\Universal\\UTCTime' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php',
'FG\\ASN1\\Universal\\UTF8String' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php',
'FG\\ASN1\\Universal\\UniversalString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php',
'FG\\ASN1\\Universal\\VisibleString' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php',
'FG\\ASN1\\UnknownConstructedObject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php',
'FG\\ASN1\\UnknownObject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/ASN1/UnknownObject.php',
'FG\\X509\\AlgorithmIdentifier' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php',
'FG\\X509\\CSR\\Attributes' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/CSR/Attributes.php',
'FG\\X509\\CSR\\CSR' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/CSR/CSR.php',
'FG\\X509\\CertificateExtensions' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/CertificateExtensions.php',
'FG\\X509\\CertificateSubject' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/CertificateSubject.php',
'FG\\X509\\PrivateKey' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/PrivateKey.php',
'FG\\X509\\PublicKey' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/PublicKey.php',
'FG\\X509\\SAN\\DNSName' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/SAN/DNSName.php',
'FG\\X509\\SAN\\IPAddress' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php',
'FG\\X509\\SAN\\SubjectAlternativeNames' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php',
'GuzzleHttp\\Client' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Client.php',
'GuzzleHttp\\ClientInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/ClientInterface.php',
'GuzzleHttp\\Cookie\\CookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php',
'GuzzleHttp\\Cookie\\CookieJarInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php',
'GuzzleHttp\\Cookie\\FileCookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php',
'GuzzleHttp\\Cookie\\SessionCookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php',
'GuzzleHttp\\Cookie\\SetCookie' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/SetCookie.php',
'GuzzleHttp\\Exception\\BadResponseException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/BadResponseException.php',
'GuzzleHttp\\Exception\\ClientException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ClientException.php',
'GuzzleHttp\\Exception\\ConnectException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ConnectException.php',
'GuzzleHttp\\Exception\\GuzzleException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/GuzzleException.php',
'GuzzleHttp\\Exception\\RequestException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/RequestException.php',
'GuzzleHttp\\Exception\\SeekException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/SeekException.php',
'GuzzleHttp\\Exception\\ServerException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ServerException.php',
'GuzzleHttp\\Exception\\TooManyRedirectsException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php',
'GuzzleHttp\\Exception\\TransferException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/TransferException.php',
'GuzzleHttp\\HandlerStack' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/HandlerStack.php',
'GuzzleHttp\\Handler\\CurlFactory' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlFactory.php',
'GuzzleHttp\\Handler\\CurlFactoryInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php',
'GuzzleHttp\\Handler\\CurlHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php',
'GuzzleHttp\\Handler\\CurlMultiHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php',
'GuzzleHttp\\Handler\\EasyHandle' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php',
'GuzzleHttp\\Handler\\MockHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/MockHandler.php',
'GuzzleHttp\\Handler\\Proxy' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/Proxy.php',
'GuzzleHttp\\Handler\\StreamHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php',
'GuzzleHttp\\MessageFormatter' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatter.php',
'GuzzleHttp\\Middleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Middleware.php',
'GuzzleHttp\\Pool' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Pool.php',
'GuzzleHttp\\PrepareBodyMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php',
'GuzzleHttp\\Promise\\AggregateException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/AggregateException.php',
'GuzzleHttp\\Promise\\CancellationException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/CancellationException.php',
'GuzzleHttp\\Promise\\Coroutine' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Coroutine.php',
'GuzzleHttp\\Promise\\EachPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/EachPromise.php',
'GuzzleHttp\\Promise\\FulfilledPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/FulfilledPromise.php',
'GuzzleHttp\\Promise\\Promise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Promise.php',
'GuzzleHttp\\Promise\\PromiseInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/PromiseInterface.php',
'GuzzleHttp\\Promise\\PromisorInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/PromisorInterface.php',
'GuzzleHttp\\Promise\\RejectedPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/RejectedPromise.php',
'GuzzleHttp\\Promise\\RejectionException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/RejectionException.php',
'GuzzleHttp\\Promise\\TaskQueue' => __DIR__ . '/..' . '/guzzlehttp/promises/src/TaskQueue.php',
'GuzzleHttp\\Promise\\TaskQueueInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/TaskQueueInterface.php',
'GuzzleHttp\\Psr7\\AppendStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/AppendStream.php',
'GuzzleHttp\\Psr7\\BufferStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/BufferStream.php',
'GuzzleHttp\\Psr7\\CachingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/CachingStream.php',
'GuzzleHttp\\Psr7\\DroppingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/DroppingStream.php',
'GuzzleHttp\\Psr7\\FnStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/FnStream.php',
'GuzzleHttp\\Psr7\\InflateStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/InflateStream.php',
'GuzzleHttp\\Psr7\\LazyOpenStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LazyOpenStream.php',
'GuzzleHttp\\Psr7\\LimitStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LimitStream.php',
'GuzzleHttp\\Psr7\\MessageTrait' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MessageTrait.php',
'GuzzleHttp\\Psr7\\MultipartStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MultipartStream.php',
'GuzzleHttp\\Psr7\\NoSeekStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/NoSeekStream.php',
'GuzzleHttp\\Psr7\\PumpStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/PumpStream.php',
'GuzzleHttp\\Psr7\\Request' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Request.php',
'GuzzleHttp\\Psr7\\Response' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Response.php',
'GuzzleHttp\\Psr7\\ServerRequest' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/ServerRequest.php',
'GuzzleHttp\\Psr7\\Stream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Stream.php',
'GuzzleHttp\\Psr7\\StreamDecoratorTrait' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/StreamDecoratorTrait.php',
'GuzzleHttp\\Psr7\\StreamWrapper' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/StreamWrapper.php',
'GuzzleHttp\\Psr7\\UploadedFile' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UploadedFile.php',
'GuzzleHttp\\Psr7\\Uri' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Uri.php',
'GuzzleHttp\\RedirectMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RedirectMiddleware.php',
'GuzzleHttp\\RequestOptions' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RequestOptions.php',
'GuzzleHttp\\RetryMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RetryMiddleware.php',
'GuzzleHttp\\TransferStats' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/TransferStats.php',
'GuzzleHttp\\UriTemplate' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/UriTemplate.php',
'Lcobucci\\JWT\\Builder' => __DIR__ . '/..' . '/lcobucci/jwt/src/Builder.php',
'Lcobucci\\JWT\\Claim' => __DIR__ . '/..' . '/lcobucci/jwt/src/Claim.php',
'Lcobucci\\JWT\\Claim\\Basic' => __DIR__ . '/..' . '/lcobucci/jwt/src/Claim/Basic.php',
'Lcobucci\\JWT\\Claim\\EqualsTo' => __DIR__ . '/..' . '/lcobucci/jwt/src/Claim/EqualsTo.php',
'Lcobucci\\JWT\\Claim\\Factory' => __DIR__ . '/..' . '/lcobucci/jwt/src/Claim/Factory.php',
'Lcobucci\\JWT\\Claim\\GreaterOrEqualsTo' => __DIR__ . '/..' . '/lcobucci/jwt/src/Claim/GreaterOrEqualsTo.php',
'Lcobucci\\JWT\\Claim\\LesserOrEqualsTo' => __DIR__ . '/..' . '/lcobucci/jwt/src/Claim/LesserOrEqualsTo.php',
'Lcobucci\\JWT\\Claim\\Validatable' => __DIR__ . '/..' . '/lcobucci/jwt/src/Claim/Validatable.php',
'Lcobucci\\JWT\\Parser' => __DIR__ . '/..' . '/lcobucci/jwt/src/Parser.php',
'Lcobucci\\JWT\\Parsing\\Decoder' => __DIR__ . '/..' . '/lcobucci/jwt/src/Parsing/Decoder.php',
'Lcobucci\\JWT\\Parsing\\Encoder' => __DIR__ . '/..' . '/lcobucci/jwt/src/Parsing/Encoder.php',
'Lcobucci\\JWT\\Signature' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signature.php',
'Lcobucci\\JWT\\Signer' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer.php',
'Lcobucci\\JWT\\Signer\\BaseSigner' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/BaseSigner.php',
'Lcobucci\\JWT\\Signer\\Ecdsa' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Ecdsa.php',
'Lcobucci\\JWT\\Signer\\Ecdsa\\KeyParser' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Ecdsa/KeyParser.php',
'Lcobucci\\JWT\\Signer\\Ecdsa\\Sha256' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Ecdsa/Sha256.php',
'Lcobucci\\JWT\\Signer\\Ecdsa\\Sha384' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Ecdsa/Sha384.php',
'Lcobucci\\JWT\\Signer\\Ecdsa\\Sha512' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Ecdsa/Sha512.php',
'Lcobucci\\JWT\\Signer\\Hmac' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Hmac.php',
'Lcobucci\\JWT\\Signer\\Hmac\\Sha256' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Hmac/Sha256.php',
'Lcobucci\\JWT\\Signer\\Hmac\\Sha384' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Hmac/Sha384.php',
'Lcobucci\\JWT\\Signer\\Hmac\\Sha512' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Hmac/Sha512.php',
'Lcobucci\\JWT\\Signer\\Key' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Key.php',
'Lcobucci\\JWT\\Signer\\Keychain' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Keychain.php',
'Lcobucci\\JWT\\Signer\\Rsa' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Rsa.php',
'Lcobucci\\JWT\\Signer\\Rsa\\Sha256' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Rsa/Sha256.php',
'Lcobucci\\JWT\\Signer\\Rsa\\Sha384' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Rsa/Sha384.php',
'Lcobucci\\JWT\\Signer\\Rsa\\Sha512' => __DIR__ . '/..' . '/lcobucci/jwt/src/Signer/Rsa/Sha512.php',
'Lcobucci\\JWT\\Token' => __DIR__ . '/..' . '/lcobucci/jwt/src/Token.php',
'Lcobucci\\JWT\\ValidationData' => __DIR__ . '/..' . '/lcobucci/jwt/src/ValidationData.php',
'Portier\\Client\\AbstractStore' => __DIR__ . '/../..' . '/src/AbstractStore.php',
'Portier\\Client\\Client' => __DIR__ . '/../..' . '/src/Client.php',
'Portier\\Client\\RedisStore' => __DIR__ . '/../..' . '/src/RedisStore.php',
'Portier\\Client\\StoreInterface' => __DIR__ . '/../..' . '/src/StoreInterface.php',
'Psr\\Http\\Message\\MessageInterface' => __DIR__ . '/..' . '/psr/http-message/src/MessageInterface.php',
'Psr\\Http\\Message\\RequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/RequestInterface.php',
'Psr\\Http\\Message\\ResponseInterface' => __DIR__ . '/..' . '/psr/http-message/src/ResponseInterface.php',
'Psr\\Http\\Message\\ServerRequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/ServerRequestInterface.php',
'Psr\\Http\\Message\\StreamInterface' => __DIR__ . '/..' . '/psr/http-message/src/StreamInterface.php',
'Psr\\Http\\Message\\UploadedFileInterface' => __DIR__ . '/..' . '/psr/http-message/src/UploadedFileInterface.php',
'Psr\\Http\\Message\\UriInterface' => __DIR__ . '/..' . '/psr/http-message/src/UriInterface.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitbff66b1076919b6333f2c7a27bd72760::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitbff66b1076919b6333f2c7a27bd72760::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitbff66b1076919b6333f2c7a27bd72760::$classMap;
}, null, ClassLoader::class);
}
}

View file

@ -0,0 +1,416 @@
[
{
"name": "spomky-labs/base64url",
"version": "v1.0.2",
"version_normalized": "1.0.2.0",
"source": {
"type": "git",
"url": "https://github.com/Spomky-Labs/base64url.git",
"reference": "ef6d5fb93894063d9cee996022259fd08d6646ea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Spomky-Labs/base64url/zipball/ef6d5fb93894063d9cee996022259fd08d6646ea",
"reference": "ef6d5fb93894063d9cee996022259fd08d6646ea",
"shasum": ""
},
"require": {
"php": "^5.3|^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0|^5.0",
"satooshi/php-coveralls": "^1.0"
},
"time": "2016-01-21T19:50:30+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Base64Url\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Florent Morselli",
"homepage": "https://github.com/Spomky-Labs/base64url/contributors"
}
],
"description": "Base 64 URL Safe Encoding/decoding PHP Library",
"homepage": "https://github.com/Spomky-Labs/base64url",
"keywords": [
"base64",
"rfc4648",
"safe",
"url"
]
},
{
"name": "fgrosse/phpasn1",
"version": "1.5.2",
"version_normalized": "1.5.2.0",
"source": {
"type": "git",
"url": "https://github.com/fgrosse/PHPASN1.git",
"reference": "a18b162eca6aa70f8f15615f4ac8c852dffbc7dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/a18b162eca6aa70f8f15615f4ac8c852dffbc7dd",
"reference": "a18b162eca6aa70f8f15615f4ac8c852dffbc7dd",
"shasum": ""
},
"require": {
"ext-gmp": "*",
"php": ">=5.6.0"
},
"require-dev": {
"phpunit/phpunit": "~4.5",
"satooshi/php-coveralls": "dev-master"
},
"suggest": {
"php-curl": "For loading OID information from the web if they have not bee defined statically"
},
"time": "2016-10-29T15:46:46+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"FG\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Friedrich Große",
"email": "friedrich.grosse@gmail.com",
"homepage": "https://github.com/FGrosse",
"role": "Author"
},
{
"name": "All contributors",
"homepage": "https://github.com/FGrosse/PHPASN1/contributors"
}
],
"description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.",
"homepage": "https://github.com/FGrosse/PHPASN1",
"keywords": [
"DER",
"asn.1",
"asn1",
"ber",
"binary",
"decoding",
"encoding",
"x.509",
"x.690",
"x509",
"x690"
]
},
{
"name": "lcobucci/jwt",
"version": "3.2.1",
"version_normalized": "3.2.1.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/ddce703826f9c5229781933b1a39069e38e6a0f3",
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"php": ">=5.5"
},
"require-dev": {
"mdanter/ecc": "~0.3.1",
"mikey179/vfsstream": "~1.5",
"phpmd/phpmd": "~2.2",
"phpunit/php-invoker": "~1.1",
"phpunit/phpunit": "~4.5",
"squizlabs/php_codesniffer": "~2.3"
},
"suggest": {
"mdanter/ecc": "Required to use Elliptic Curves based algorithms."
},
"time": "2016-10-31T20:09:32+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Luís Otávio Cobucci Oblonczyk",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"keywords": [
"JWS",
"jwt"
]
},
{
"name": "guzzlehttp/promises",
"version": "v1.3.1",
"version_normalized": "1.3.1.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"shasum": ""
},
"require": {
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
},
"time": "2016-12-20T10:07:11+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
]
},
{
"name": "psr/http-message",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2016-08-06T14:39:51+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
]
},
{
"name": "guzzlehttp/psr7",
"version": "1.3.1",
"version_normalized": "1.3.1.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b",
"reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"time": "2016-06-24T23:00:38+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "PSR-7 message implementation",
"keywords": [
"http",
"message",
"stream",
"uri"
]
},
{
"name": "guzzlehttp/guzzle",
"version": "6.2.2",
"version_normalized": "6.2.2.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60",
"reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60",
"shasum": ""
},
"require": {
"guzzlehttp/promises": "^1.0",
"guzzlehttp/psr7": "^1.3.1",
"php": ">=5.5"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.0",
"psr/log": "^1.0"
},
"time": "2016-10-08T15:01:37+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.2-dev"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
]
}
]

View file

@ -0,0 +1 @@
src_dir: lib

View file

@ -0,0 +1,11 @@
# Project and backup files
*~
*.project
.settings/*
.idea
# Composer
vendor
# Test coverage reports
clover.xml

View file

@ -0,0 +1,13 @@
preset: symfony
linting: true
disabled:
- empty_return
- phpdoc_align
- blankline_after_open_tag
- pre_increment
- return
- unalign_equals
- unalign_double_arrow
- phpdoc_separation

View file

@ -0,0 +1,21 @@
language: php
sudo: false
php:
- 5.6
- 7
- hhvm
cache:
directories:
- vendor
before_script:
- composer install
script:
- vendor/bin/phpunit --coverage-clover build/logs/clover.xml
after_script:
# code coverage currently does not seem to be supported on travis php 7 or hhvm
- vendor/bin/coveralls --verbose

View file

@ -0,0 +1,25 @@
#### v.1.5.2 (2016-10-29)
* allow empty octet strings
#### v.1.5.1 (2015-10-02)
* add keywords to composer.json (this is a version on its own so the keywords are found on a stable version at packagist.org)
#### v.1.5.0 (2015-10-30)
* fix a bug that would prevent you from decoding context specific tags on multiple objects [#57](https://github.com/fgrosse/PHPASN1/issues/57)
- `ExplicitlyTaggedObject::__construct` does now accept multiple objects to be tagged with a single tag
- `ExplicitlyTaggedObject::getContent` will now always return an array (even if only one object is tagged)
#### v.1.4.2 (2015-09-29)
* fix a bug that would prevent you from decoding empty tagged objects [#57](https://github.com/fgrosse/PHPASN1/issues/57)
#### v.1.4.1
* improve exception messages and general error handling [#55](https://github.com/fgrosse/PHPASN1/pull/55)
#### v.1.4.0
* **require PHP 5.6**
* support big integers (closes #1 and #37)
* enforce one code style via [styleci.io][9]
* track code coverage via [coveralls.io][10]
* replace obsolete `FG\ASN1\Exception\GeneralException` with `\Exception`
* `Construct` (`Sequence`, `Set`) does now implement `ArrayAccess`, `Countable` and `Iterator` so its easier to use
* add [`TemplateParser`][11]

View file

@ -0,0 +1,19 @@
Copyright (c) 2012-2015 Friedrich Große <friedrich.grosse@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,163 @@
PHPASN1
=======
[![Build Status](https://secure.travis-ci.org/fgrosse/PHPASN1.png?branch=master)](http://travis-ci.org/fgrosse/PHPASN1)
[![HHVM Status](http://hhvm.h4cc.de/badge/fgrosse/phpasn1.png)](http://hhvm.h4cc.de/package/fgrosse/phpasn1)
[![PHP 7 ready](http://php7ready.timesplinter.ch/fgrosse/PHPASN1/badge.svg)](https://travis-ci.org/fgrosse/PHPASN1)
[![Coverage Status](https://coveralls.io/repos/fgrosse/PHPASN1/badge.svg?branch=master&service=github)](https://coveralls.io/github/fgrosse/PHPASN1?branch=master)
[![Latest Stable Version](https://poser.pugx.org/fgrosse/phpasn1/v/stable.png)](https://packagist.org/packages/fgrosse/phpasn1)
[![Total Downloads](https://poser.pugx.org/fgrosse/phpasn1/downloads.png)](https://packagist.org/packages/fgrosse/phpasn1)
[![Latest Unstable Version](https://poser.pugx.org/fgrosse/phpasn1/v/unstable.png)](https://packagist.org/packages/fgrosse/phpasn1)
[![License](https://poser.pugx.org/fgrosse/phpasn1/license.png)](https://packagist.org/packages/fgrosse/phpasn1)
A PHP Framework that allows you to encode and decode arbitrary [ASN.1][3] structures
using the [ITU-T X.690 Encoding Rules][4].
This encoding is very frequently used in [X.509 PKI environments][5] or the communication between heterogeneous computer systems.
The API allows you to encode ASN.1 structures to create binary data such as certificate
signing requests (CSR), X.509 certificates or certificate revocation lists (CRL).
PHPASN1 can also read [BER encoded][6] binary data into separate PHP objects that can be manipulated by the user and reencoded afterwards.
The **changelog** can now be found at [CHANGELOG.md](CHANGELOG.md).
## Dependencies
PHPASN1 requires at least `PHP 5.6` and the `gmp` extension.
It has also been successfully tested using `PHP 7` and `HHVM`
For the loading of object identifier names directly from the web [curl][7] is used.
## Installation
The preferred way to install this library is to rely on [Composer][2]:
```bash
$ composer require fgrosse/phpasn1
```
## Usage
### Encoding ASN.1 Structures
PHPASN1 offers you a class for each of the implemented ASN.1 universal types.
The constructors should be pretty self explanatory so you should have no big trouble getting started.
All data will be encoded using [DER encoding][8]
```php
use FG\ASN1\OID;
use FG\ASN1\Universal\Integer;
use FG\ASN1\Universal\Boolean;
use FG\ASN1\Universal\Enumerated;
use FG\ASN1\Universal\IA5String;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\PrintableString;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\Set;
use FG\ASN1\Universal\NullObject;
$integer = new Integer(123456);
$boolean = new Boolean(true);
$enum = new Enumerated(1);
$ia5String = new IA5String('Hello world');
$asnNull = new NullObject();
$objectIdentifier1 = new ObjectIdentifier('1.2.250.1.16.9');
$objectIdentifier2 = new ObjectIdentifier(OID::RSA_ENCRYPTION);
$printableString = new PrintableString('Foo bar');
$sequence = new Sequence($integer, $boolean, $enum, $ia5String);
$set = new Set($sequence, $asnNull, $objectIdentifier1, $objectIdentifier2, $printableString);
$myBinary = $sequence->getBinary();
$myBinary .= $set->getBinary();
echo base64_encode($myBinary);
```
### Decoding binary data
Decoding BER encoded binary data is just as easy as encoding it:
```php
use FG\ASN1\Object;
$base64String = ...
$binaryData = base64_decode($base64String);
$asnObject = Object::fromBinary($binaryData);
// do stuff
```
If you already know exactly how your expected data should look like you can use the `FG\ASN1\TemplateParser`:
```php
use FG\ASN1\TemplateParser;
// first define your template
$template = [
Identifier::SEQUENCE => [
Identifier::SET => [
Identifier::OBJECT_IDENTIFIER,
Identifier::SEQUENCE => [
Identifier::INTEGER,
Identifier::BITSTRING,
]
]
]
];
// if your binary data is not matching the template you provided this will throw an `\Exception`:
$parser = new TemplateParser();
$object = $parser->parseBinary($data, $template);
// there is also a convenience function if you parse binary data from base64:
$object = $parser->parseBase64($data, $template);
```
You can use this function to make sure your data has exactly the format you are expecting.
### Navigating decoded data
All constructed classes (i.e. `Sequence` and `Set`) can be navigated by array access or using an iterator.
You can find examples
[here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/Universal/SequenceTest.php#L148-148),
[here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/Universal/SequenceTest.php#L121) and
[here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/TemplateParserTest.php#L45).
### Give me more examples!
To see some example usage of the API classes or some generated output check out the [examples](https://github.com/fgrosse/PHPASN1/tree/master/examples).
### How do I contribute?
If you found an issue or have a question submit a github issue with detailed information.
In case you already know what caused the issue and feel in the mood to fix it, your code contributions are always welcome. Just fork the repository, implement your changes and make sure that you covered everything with tests.
Afterwards submit a pull request via github and be a little patient :) I usually try to comment and/or merge as soon as possible.
#### Mailing list
New features or questions can be discussed in [this google group/mailing list][12].
### Thanks
To [all contributors][1] so far!
## License
This library is distributed under the [MIT License](LICENSE).
[1]: https://github.com/fgrosse/PHPASN1/graphs/contributors
[2]: https://getcomposer.org/
[3]: http://www.itu.int/ITU-T/asn1/
[4]: http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=x.690
[5]: http://en.wikipedia.org/wiki/X.509
[6]: http://en.wikipedia.org/wiki/X.690#BER_encoding
[7]: http://php.net/manual/en/book.curl.php
[8]: http://en.wikipedia.org/wiki/X.690#DER_encoding
[9]: https://styleci.io
[10]: https://coveralls.io/github/fgrosse/PHPASN1
[11]: https://github.com/fgrosse/PHPASN1/blob/master/tests/ASN1/TemplateParserTest.php#L16
[12]: https://groups.google.com/d/forum/phpasn1

View file

@ -0,0 +1,47 @@
{
"name": "fgrosse/phpasn1",
"description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.",
"type": "library",
"homepage": "https://github.com/FGrosse/PHPASN1",
"license": "MIT",
"authors": [
{
"name": "Friedrich Große",
"email": "friedrich.grosse@gmail.com",
"homepage": "https://github.com/FGrosse",
"role": "Author"
},
{
"name": "All contributors",
"homepage": "https://github.com/FGrosse/PHPASN1/contributors"
}
],
"keywords": [ "x690", "x.690", "x.509", "x509", "asn1", "asn.1", "ber", "der", "binary", "encoding", "decoding" ],
"require": {
"php": ">=5.6.0",
"ext-gmp": "*"
},
"require-dev": {
"phpunit/phpunit": "~4.5",
"satooshi/php-coveralls": "dev-master"
},
"suggest": {
"php-curl": "For loading OID information from the web if they have not bee defined statically"
},
"autoload": {
"psr-4": {
"FG\\": "lib/"
}
},
"autoload-dev": {
"psr-4": {
"FG\\Test\\": "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.5.x-dev"
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,129 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require_once __DIR__.'/../vendor/autoload.php';
require_once 'shared.php';
use FG\ASN1\OID;
use FG\ASN1\Object;
use FG\ASN1\Identifier;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\OctetString;
use FG\ASN1\Universal\Sequence;
$base64 = 'MIIINTCCBh2gAwIBAgIQbVVJc10C7UUjfYRHQ//7nTANBgkqhkiG9w0BAQsFADB0
MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDEtMCsGA1UECxMkQ2Vy
dGlzaWduIENlcnRpZmljYWRvcmEgRGlnaXRhbCBTLkEuMSEwHwYDVQQDExhBQyBD
ZXJ0aXNpZ24gTXVsdGlwbGEgRzUwHhcNMTIwNTI0MDAwMDAwWhcNMTMwNTIzMjM1
OTU5WjCBwDELMAkGA1UEBhMCQlIxEzARBgNVBAoUCklDUC1CcmFzaWwxIjAgBgNV
BAsUGUF1dGVudGljYWRvIHBvciBBUiBEYXNjaGkxGzAZBgNVBAsUEkFzc2luYXR1
cmEgVGlwbyBBMTEVMBMGA1UECxQMSUQgLSAzMTYzNjAwMRwwGgYDVQQDExNGaW1h
dGVjIFRleHRpbCBMdGRhMSYwJAYJKoZIhvcNAQkBFhdmZXJuYW5kb0BmaW1hdGVj
LmNvbS5icjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOqNCGrNF3aV
+lfImn2ji7SsyUbwqyddd2GRnZBUv3lOBQ9DhXq+YX1qjkb/EgGwC9EhIermj3Xw
SZ4PjOweubXAVZ8BY2CrjZhjOOQRq5IKvbqStbsYSEOvOyVfbsDuYdralM6SJpyW
wJRdjTprbKUARU2fJBLJgiGGyIwh0KqAIhp3OcwCW9t4B5RT01fcmPa6hhU808dQ
JgqykA1i8ylQjtXzg52tinUOl6zWZZxxkg5y9l73Lf14eDFHcnI4lFv4kS3jkdk8
mwyll2K0nQX+aMcli8aN3K7C/Lj4VQIWuGarVNfzwg3wP3WzzOS+go0UFZ8ZSy52
3kpfnlsNBK0CAwEAAaOCA3QwggNwMIG6BgNVHREEgbIwga+gPQYFYEwBAwSgNAQy
MjAxMDE5NTkwNTIyNTUzNzg4MDAwMDAwMDAwMDAwMDAwMDAwMDg4NzQzNzJ4c3Nw
U1CgIQYFYEwBAwKgGAQWRmVybmFuZG8gSm9zZSBLYWlyYWxsYaAZBgVgTAEDA6AQ
BA41ODcxNjUyMzAwMDExOaAXBgVgTAEDB6AOBAwwMDAwMDAwMDAwMDCBF2Zlcm5h
bmRvQGZpbWF0ZWMuY29tLmJyMAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUnVDPvf8k
yq+xM+sX4kJ6jmkqjlMwDgYDVR0PAQH/BAQDAgXgMIGJBgNVHSAEgYEwfzB9BgZg
TAECAQswczBxBggrBgEFBQcCARZlaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWdu
LmNvbS5ici9yZXBvc2l0b3Jpby9kcGMvQUNfQ2VydGlzaWduX011bHRpcGxhL0RQ
Q19BQ19DZXJ0aVNpZ25NdWx0aXBsYS5wZGYwggElBgNVHR8EggEcMIIBGDBcoFqg
WIZWaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNvbS5ici9yZXBvc2l0b3Jp
by9sY3IvQUNDZXJ0aXNpZ25NdWx0aXBsYUc1L0xhdGVzdENSTC5jcmwwW6BZoFeG
VWh0dHA6Ly9pY3AtYnJhc2lsLm91dHJhbGNyLmNvbS5ici9yZXBvc2l0b3Jpby9s
Y3IvQUNDZXJ0aXNpZ25NdWx0aXBsYUc1L0xhdGVzdENSTC5jcmwwW6BZoFeGVWh0
dHA6Ly9yZXBvc2l0b3Jpby5pY3BicmFzaWwuZ292LmJyL2xjci9DZXJ0aXNpZ24v
QUNDZXJ0aXNpZ25NdWx0aXBsYUc1L0xhdGVzdENSTC5jcmwwHQYDVR0lBBYwFAYI
KwYBBQUHAwIGCCsGAQUFBwMEMIGgBggrBgEFBQcBAQSBkzCBkDBkBggrBgEFBQcw
AoZYaHR0cDovL2ljcC1icmFzaWwuY2VydGlzaWduLmNvbS5ici9yZXBvc2l0b3Jp
by9jZXJ0aWZpY2Fkb3MvQUNfQ2VydGlzaWduX011bHRpcGxhX0c1LnA3YzAoBggr
BgEFBQcwAYYcaHR0cDovL29jc3AuY2VydGlzaWduLmNvbS5icjANBgkqhkiG9w0B
AQsFAAOCAgEAyfdKIKDLGZyOhAsycAgJ7RZyn4/2tN29bd+rLwtkvp/9XJGRCAob
fkLU8UACGzbVHT/bB7sOKIiHvp8CgGyoU/1veo5a1yGMFQobQWGcdEYBOd+6Ax6U
FEOc+Ti+LsXsUA5tRbzTwfXJFFOIgFDgrXl7V4JGjSI3GdyoiEECR9OfM/UFYtr/
pD194Xh6G0lSCuP9xOKpOJ7qk24mcfObcGxooMxmktkt0bevzx5w84RtXJfbube+
p6o7JP06T6JJTGfIUfNKYU8wJ6CV9eUHEMDLHMoH6wnYK7d8rHcAbSoTJGMFlSYZ
OqJrWAbKAPjraFNeNaH675u//Ck127kJVidcuRwtkLZV/OIlQaw/4QSp90QWQOQg
AsOWC9+h8v/RcCYTJpWM22MCq3Xk67nz+mXO8e7LKpzHEh2sjX3gkfw4h80zYfT7
kTsNXVdxAHXiIahKNeRUT8fGhxvyOA0RqAXQBUaOyLyGYWRJ7Is5IqAAU6XiBHYe
oJ3v8BTGYzIK2Ud5dZ23yzBp2ejdQzQX1ETpJoEdgELToHmfBXTkI+7ne59wSRkH
beQXoK5y0U3gh1vIz/53GG0QMuCvq9r9xTMERcFJcpQUxJ2RjwxIFHlIFbNwCeiW
I3DmZaXdR169kRoPIqkV3QIREs/yHBvkxXrwDt416stUnQ8KsxAEatE=';
try {
$binaryData = base64_decode($base64);
//hexdump($binaryData, false);
$rootObject = Object::fromBinary($binaryData);
//printObject($rootObject);
// first navigate to the certificate extensions
// (see ITU-T X.509 section 7 "Public-keys and public-key certificates" for cert structure)
/* @var Sequence $rootObject */
assert($rootObject->getType() == Identifier::SEQUENCE);
$topLevelContainer = $rootObject->getChildren();
$certificateInfo = $topLevelContainer[0];
/* @var Sequence $certificateInfo */
assert($certificateInfo->getType() == Identifier::SEQUENCE);
// there need to be at least 8 child elements if the certificate extensions field is present
assert($certificateInfo->getNumberofChildren() >= 8);
$certInfoFields = $certificateInfo->getChildren();
$certExtensions = $certInfoFields[7];
// check if this is really the certificate extensions sequence
/* @var \FG\ASN1\Object $certExtensions */
$certExtensionsType = $certExtensions->getType();
assert(Identifier::isContextSpecificClass($certExtensionsType));
assert(Identifier::getTagNumber($certExtensions->getType()) == 3);
// this should contain a sequence of extensions
$certExtensions = $certExtensions->getFirstChild();
assert($certExtensions->getType() == Identifier::SEQUENCE);
// now check all extensions and search for the SAN
/** @var \FG\ASN1\Object $extensionSequence */
foreach ($certExtensions as $extensionSequence) {
assert($extensionSequence->getType() == Identifier::SEQUENCE);
assert($extensionSequence->getNumberofChildren() >= 2);
$extensionSequenceChildren = $extensionSequence->getChildren();
$objectIdentifier = $extensionSequenceChildren[0];
/* @var ObjectIdentifier $objectIdentifier */
assert($objectIdentifier->getType() == Identifier::OBJECT_IDENTIFIER);
if ($objectIdentifier->getContent() == OID::CERT_EXT_SUBJECT_ALT_NAME) {
// now we have the wanted octet string
$octetString = $extensionSequenceChildren[1];
/* @var OctetString $octetString */
$octetStringBinary = $octetString->getBinaryContent();
// At this point you may want to create the sequence from the binary value of
// the octet string and parse its structure like we did so far.
// However a more general approach would be to understand the format of the
// contained SAN fields and implement them in SubjectAlternativeNames.
$sequence = Sequence::fromBinary($octetStringBinary);
echo 'This is the parsed content of the SAN certificate extension field so far:'.PHP_EOL;
printObject($sequence);
// The following does not work yet because PHPASN1 SAn does only support DNS and IP
//SubjectAlternativeNames::fromBinary($octetStringBinary);
}
}
} catch (\Exception $exception) {
echo '[ERROR] Caught exception:'.PHP_EOL.$exception->getMessage().PHP_EOL;
}

View file

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use FG\ASN1\Object;
require_once __DIR__.'/../vendor/autoload.php';
require_once 'shared.php';
$hex = 'a02b302906092a864886f70d01090e311c301a30180603551d110411300f820d636f72766573706163652e6465';
$asn = Object::fromBinary(hex2bin($hex));
printObject($asn);

View file

@ -0,0 +1,77 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use FG\ASN1\ExplicitlyTaggedObject;
use FG\ASN1\Universal\Enumerated;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\Sequence;
require_once __DIR__.'/../vendor/autoload.php';
// $data has been generated using https://pkijs.org/examples/OCSP_resp_complex_example.html
$data = 'MIIEXgoBAKCCBFcwggRTBgkrBgEFBQcwAQEEggREMIIEQDBeoSAwHjEcMAkGA1UE
BhMCUlUwDwYDVQQDHggAVABlAHMAdBgPMjAxNTA3MTYxNzU0MjFaMCkwJzASMAcG
BSsOAwIaBAEBBAEBAgEBgAAYDzIwMTUwNzE2MTc1NDIxWjALBgkqhkiG9w0BAQUD
ggEBAG5B1xOnhgzgpsnspWd9c4eLIeOY1XXl7q2DUO2kGji4WbBXtWDMEv7QQO9/
8devmyWNTFlkScbhiMPIEfoES6AyW8lZu9aON2tMssgj/Ev9+H+A2Z2WHQdeDtv7
AS2mYa/7e3Ucsb63pAmBcxgDY55eHwmivBfZIWF6RjbzuJ0Uxz6NvrVEHlWz2KE0
1n/7qbcwbHx8nu1s/IdswFG9iePm5sTsHPsbvsMQ5ZfkvkuL/t5WlLONlRYunOA4
Nytf2lUIZbZznB0dvQG4vm1F9IQEcv1V0aU0D/ZU81I7lSPlPEv/DMTEo1vePTZv
Gv8S1K+keDD/bfwM22d663Vfmt+gggLKMIICxjCCAsIwggGsoAMCAQICAQEwCwYJ
KoZIhvcNAQEFMB4xHDAJBgNVBAYTAlJVMA8GA1UEAx4IAFQAZQBzAHQwHhcNMTMw
MjAxMDAwMDAwWhcNMTYwMjAxMDAwMDAwWjAeMRwwCQYDVQQGEwJSVTAPBgNVBAMe
CABUAGUAcwB0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsFlz/3yl
mk+8uP4/x61UWBliqW08/1jMA83Qj2yuCAvhMhmuq8BvyWP4jnXoxTy3iTZFDXIj
5Vg2FlHWna0TMsU5jlmv8W/mu9U3+q7i1+339JrEZkrDLNDpJ23gqKBgC1OaNH7+
vicBRmoP5kj2X62XMaKT5TMFBZzTtSF7IaUjt9SVXQjG3aHIy7D1mOypvEw4LSS+
RvvJTezSQeCoLpX7HziRnoVUNkJWZHL2wG5rb/SJzpXwHkRa7R250vN8TFtSSsp4
YXzK6aks/qkxRJ1UkBLUAZlRasUD+zu8gyTIz0sNgFrOtx8EOzjZRiJ/vBiVOn88
Brf4i0D+fVXmJQIDAQABow8wDTALBgNVHQ8EBAMCAAIwCwYJKoZIhvcNAQEFA4IB
AQBfubKPTvmDGrDxoqCbPwFoPRC0STwPL2GV8f5sD/Sbyc0NoJdygUO2DvquGGn5
6UJCfLo1u6Dn4zuuDs3m6if86HTpAf9Z3a72ok2Tor/NFwYt+vDOrFY5F4bXDZkf
u4zuDLmjpj26Dk4te3BVohsLTXbvJ5a/TT2VanwNOyx85lXPxy3V8Rr1AwlmHZoz
DDbUGbe/noUDJCgMjvaKKvLykIhIcW+g6W7SOcKRflw5H8kzDv816XFODSC3X1Uw
o3aVy9du/0mH+g4HvyVVplO90tdoHD1gHUMZwuen4dbTzhWv4dtLFelWM5lGWbLE
Wn7kJghclgIxv10nkGyfrowt';
// OCSP response status according to
// https://tools.ietf.org/html/rfc6960#section-4.2.1
$validResponseStatuses = array(
0 => 'Response has valid confirmations',
1 => 'Illegal confirmation request',
2 => 'Internal error in issuer',
3 => 'Try again later',
// (4) is not used
5 => 'Must sign the request',
6 => 'Request unauthorized',
);
$ocspResponse = Sequence::fromBinary(base64_decode($data));
/* @var Enumerated $responseStatus */
$elements = $ocspResponse->getChildren();
$responseStatus = $elements[0];
$responseStatusCode = $responseStatus->getContent();
echo PHP_EOL;
echo "OCSP response status: $responseStatusCode ({$validResponseStatuses[$responseStatusCode]})".PHP_EOL;
/** @var ExplicitlyTaggedObject $responseBytes */
$responseBytes = $elements[1];
/** @var Sequence $responseBytesSequence */
$responseBytesSequence = $responseBytes->getContent();
/** @var ObjectIdentifier $responseType */
$responseType = $responseBytesSequence->getChildren()[0];
echo "ResponseType: {$responseType}".PHP_EOL;
$response = $responseBytesSequence->getChildren()[1];
echo "Response (octet string): {$response}".PHP_EOL;

View file

@ -0,0 +1,108 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use FG\ASN1\ExplicitlyTaggedObject;
use FG\ASN1\Object;
use FG\ASN1\Universal\Enumerated;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\Sequence;
require_once __DIR__.'/../vendor/autoload.php';
require_once 'shared.php';
$input1 = '
MIICWDCCAhgCAQAwVzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5LmlvMQ0wCwYDVQQK
DARQeUNBMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxDzANBgNVBAcMBkF1
c3RpbjCCAbYwggErBgcqhkjOOAQBMIIBHgKBgQCNf628CeKEqvppFUzqJBdwBJCe
UZ+LNdaFzeW07NyVg+dNNwoPiK2pjwJvJ3Yvs9XaeDb5ht/Ns1ieW5Jb6hFN78A+
+B2uMMJLvG3z1YjpNCe7pkID1KWxaHsrXjtkPUxhSXb4n5WjjT5MiQZfupdRTCLF
Ctu/KJFjp0tUhZs1twIVAINd5WvQfPf4LiAy/niUmu0ReqLvAoGAH3F7Wgd4L8Lk
5o4xH+qRpU7dNrhqxjTRTwWmipfq6dLvMfse895Cw9EA35ymT1vcKux7/ftHTPgx
/qBYU7XgWfLSSYCgrEY/HoGK81I+PLeaOdRfqScxiXdShCRpz4VAsBSRAk6q+85g
GOih9GWMND9Lp8CyHlN2oh9L64SRlh4DgYQAAoGABxPwdkH2Npu1qVRSdKLUwBmY
Nn+zcbueE0NjY2cu1o+CF0wt4FyOg5vG3laN1QuijY2dhxlCOq7FVX3xDXc6si1t
Zcu4eASml7yP2WW5Uvn36FDt8TyKzbXXU7bRDlngtXMuPIK6+hQDQrxKO7oWvQaB
yKai27t+/mziuEY7FwugADAJBgcqhkjOOAQDAy8AMCwCFGHVjcAo0BEIGKfYF9dC
NXJ8Ss/fAhQJe1LhmOzpXeFyc/CpJN8jzp2BiA==';
$input2 = '
MIIFGDCCAwACAQAwOjEWMBQGCgmSJomT8ixkARkWBnNlY3VyZTEgMB4GA1UEAxMX
LlNlY3VyZSBFbnRlcnByaXNlIENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQCzgEpL+Za7a3y7YpURDrxlGIBlks25fD0tHaZIYkBTaXA5h+9MWoXn
FA7AlIUt8pbBvXdJbOCmGaeQmBfBH0Qy9vTbx/DR2IOwzqy2ZHuurI5bPL12ceE2
Mxa9xgY/i7U6MAUtoA3amEd7cKj2fz9EWZruRladOX0DXv9KexSan+45QjCWH+u2
Cxem2zH9ZDNPGBuAF9YsAvkdHdAoX8aSm05ZAjUiO2e/+L57whh7zZiDY3WIhin7
N/2JNTKVO6lx50S8a34XUKBt3SKgSR941hcLrBYUNftUYsTPo40bzKKcWqemiH+w
jQiDrln4V2b5EbVeoGWe4UDPXCVmC6UPklG7iYfF0eeK4ujV8uc9PtV2LvGLOFdm
AYE3+FAba5byQATw/DY8EJKQ7ptPigJhVe47NNeJlsKwk1haJ9k8ZazjS+vT45B5
pqe0yBFAEon8TFnOLnAOblmKO12i0zqMUNAAlmr1c8jNjLr+dhruS+QropZmzZ24
mAnFG+Y0qpfhMzAxTGQyVjyGwDfRK/ARmtrGpmROjj5+6VuMmZ6Ljf3xN09epmtH
gJe+lYNBlpfUYg16tm+OusnziYnXL6nIo2ChOY/7GNJJif9fjvvaPDCC98K64av5
5rpIx7N/XH4hwHeQQkEQangExE+8UMyBNFNmvPnIHVHUZdYo4SLsYwIDAQABoIGY
MBsGCisGAQQBgjcNAgMxDRYLNi4zLjk2MDAuMi4weQYJKoZIhvcNAQkOMWwwajAQ
BgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU5nEIMEUT5mMd1WepmviwgK7dIzww
GQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAKZl6bAeaID3b/ic4aztL8ZZI7vi
D3A9otUKx6v1Xe63zDPR+DiWSnxb9m+l8OPtnWkcLkzEIM/IMWorHKUAJ/J871D0
Qx+0/HbkcrjMtVu/dNrtb9Z9CXup66ZvxTPcpEziq0/n2yw8QdBaa+lli65Qcwcy
tzMQK6WQTRYfvVCIX9AKcPKxwx1DLH+7hL/bERB1lUDu59Jx6fQfqJrFVOY2N8c0
MGvurfoHGmEoyCMIyvmIMu4+/wSNEE/sSDp4lZ6zuF6rf1m0GiLdTX2XJE+gfvep
JTFmp4S3WFqkszKvaxBIT+jV0XKTNDwnO+dpExwU4jZUh18CdEFkIUuQb0gFF8B7
WJFVpNdsRqZRPBz83BW1Kjo0yAmaoTrGNmG0p6Qf3K2zbk1+Jik3VZq4rvKoTi20
6RvLA2//cMNfkYPsuqvoHGe2e0GOLtIB63wJzloWROpb72ohEHsvCKullIJVSuiS
9sfTBAenHCyndgAEd4T3npTUdaiNumVEm5ilZId7LAYekJhkgFu3vlcl8blBJKjE
skVTp7JpBmdXCL/G/6H2SFjca4JMOAy3DxwlGdgneIaXazHs5nBK/BgKPIyPzZ4w
secxBTTCNgI48YezK3GDkn65cmlnkt6F6Mf0MwoDaXTuB88Jycbwb5ihKnHEJIsO
draiRBZruwMPwPIP
';
$input3 = '
MIIC4zCCAcsCAQAwcjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDENMAsGA1UEAwwEdGVz
dDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAL7t6CR90kOZ0m3qJx9koHBFdXnunEq9ELSUvsZ7oFiw
6zJsyi18S9znVRLEgsW4v2rTsLiJmKnMF2Kyd9AQ2X2zKUK49CTMqG1p/4lF0oLa
nQgNmjYC4VL61wTte6CUAJX7wZA8LL00QfCdgILKodP12E3c7Yp3yw5TAOBdA3Av
KnpsUiNLmQN3HSn9dwk9rCHulrP8LL97l//H2KSCvOvSfk1XeN7ADSyWMC6ZPBD/
7WVnQo7GgMAA0Qhz+nIIFA5AyuFEbRiff+L5EEl+Bg6R9tgx+QUt9JXxF260dkAb
iKBmPcJldl2/GEmz6RAXhMwJnN35tbdzw0LVLzsypr0CAwEAAaAsMBMGCSqGSIb3
DQEJAjEGDAR0ZXN0MBUGCSqGSIb3DQEJBzEIDAYxMjM0NTYwDQYJKoZIhvcNAQEF
BQADggEBACqZ5+3GP7QbTJAqnUloMRBJGYZl0/EyW6dAMoq7E+RiR2OlJGXgP+ga
DPv7Wa4jZiYmf2CZCWKP+WvTvcg4AQq5h7hhAN+u+s8kk0lIjYjnkKRAedGYKDUO
fPD9WowISru3RB1xyxlvgeZS6WoA6TD8REVa1UyFoNzUewvsrNVkKSHh1xk/+ePx
2Ovvrcg9pAWY4K8FvMRdFQKnEud9CAoMxqz3kszhxDW6rcr2mgFPSrKi5WNj+Scg
Tqod8xbB753JWjEbG6Hui9LIMihTX3ZJ0c2GB0buhEgz49y83X/byFHSGGSQpzxX
qXDFVov9UZ+sGy8CJ5ahII79yrfKpxY=
';
try {
echo 'Input 1:' . PHP_EOL;
printObject(Object::fromBinary(base64_decode($input1)));
} catch (Exception $exception) {
echo "ERROR: " . $exception->getMessage();
}
echo PHP_EOL;
try {
echo 'Input 2:' . PHP_EOL;
printObject(Object::fromBinary(base64_decode($input2)));
} catch (Exception $exception) {
echo "ERROR: " . $exception->getMessage();
}
echo PHP_EOL;
try {
echo 'Input 3:' . PHP_EOL;
printObject(Object::fromBinary(base64_decode($input3)));
} catch (Exception $exception) {
echo "ERROR: " . $exception->getMessage();
}

View file

@ -0,0 +1,187 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require_once __DIR__.'/hexdump.php';
require_once __DIR__.'/../vendor/autoload.php';
use FG\ASN1\OID;
use FG\ASN1\Object;
use FG\ASN1\Universal\Boolean;
use FG\ASN1\Universal\Integer;
use FG\ASN1\Universal\Enumerated;
use FG\ASN1\Universal\PrintableString;
use FG\ASN1\Universal\NumericString;
use FG\ASN1\Universal\CharacterString;
use FG\ASN1\Universal\UTF8String;
use FG\ASN1\Universal\BitString;
use FG\ASN1\Universal\OctetString;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\NullObject;
use FG\ASN1\Universal\UTCTime;
use FG\ASN1\Universal\GeneralizedTime;
use FG\ASN1\Universal\Set;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Composite\AttributeTypeAndValue;
use FG\ASN1\Composite\RDNString;
use FG\ASN1\Composite\RelativeDistinguishedName;
use FG\X509\CertificateSubject;
use FG\X509\PublicKey;
use FG\X509\AlgorithmIdentifier;
use FG\X509\CSR\CSR;
// check if openssl is installed on this system
$openSSLVersionOutput = shell_exec('openssl version');
if (substr($openSSLVersionOutput, 0, 7) == 'OpenSSL') {
$openSSLisAvailable = true;
} else {
$openSSLisAvailable = false;
}
function printVariableInfo(Object $variable)
{
$className = get_class($variable);
$stringValue = nl2br($variable->__toString());
$binaryData = $variable->getBinary();
$base64Binary = chunk_split(base64_encode($binaryData), 24);
echo '<tr>';
echo "<td class='ASNclass'>{$className}</td>";
echo "<td class='toString'>\"<span class='red'>{$stringValue}</span>\"</td>";
echo "<td class='monospace base64'>{$base64Binary}</td>";
echo '<td>'.hexdump($binaryData, true, true, true).'</td>';
global $openSSLisAvailable;
if ($openSSLisAvailable) {
$openSSLOutput = shell_exec("echo '{$base64Binary}' | openssl asn1parse -inform PEM -dump -i 2>&1");
echo "<td class='openSSL'><pre>{$openSSLOutput}</pre></td>";
}
echo '</tr>';
}
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="Examples of all available PHPASN1 ASN classes">
<meta name="author" content="Friedrich Große">
<link rel="stylesheet" href="common.css"/>
<title>PHPASN1 Classes</title>
</head>
<body>
<h1>Examples for all available PHPASN1 classes</h1>
<?php
if ($openSSLisAvailable == false) {
echo "<p class='notice'>Note: OpenSSL could not be found on this system. This is absolutely no problem
for PHPASN1 but it could have been used to show you that the generated binaries are indeed correct :)</p>";
}
?>
<table>
<th>Class</th>
<th>toString()</th>
<th>Binary (base64)</th>
<th>Binary (hex)</th>
<?php
if ($openSSLisAvailable) {
echo '<th>OpenSSL asn1parse output</th>';
}
// Primitives
printVariableInfo(new Boolean(true));
printVariableInfo(new Boolean(false));
printVariableInfo(new Integer(123456));
printVariableInfo(new Integer(-546));
printVariableInfo(new Enumerated(1));
printVariableInfo(new PrintableString('Hello World'));
printVariableInfo(new NumericString('123456'));
printVariableInfo(new CharacterString('Foo'));
printVariableInfo(new UTF8String('Hello ♥♥♥ World'));
// there are more restricted character string classes available
printVariableInfo(new BitString('3082010a02820101009e2a7'));
printVariableInfo(new OctetString('x01020304AE123200A0'));
printVariableInfo(new ObjectIdentifier(OID::PKCS9_EMAIL));
printVariableInfo(new NullObject());
printVariableInfo(new UTCTime('2012-09-30 14:33'));
printVariableInfo(new GeneralizedTime('2008-07-01 22:35:17.024540+06:30'));
// Constructed
printVariableInfo(new Sequence(
new ObjectIdentifier(OID::COMMON_NAME),
new PrintableString('Friedrich')
));
printVariableInfo(new Set(
new ObjectIdentifier(OID::COUNTRY_NAME),
new PrintableString('Germany')
));
printVariableInfo(new AttributeTypeAndValue(
OID::ORGANIZATION_NAME,
new PrintableString('My Organization')
));
printVariableInfo(new RelativeDistinguishedName(
OID::RSA_ENCRYPTION,
new Integer(62342)
));
printVariableInfo(new RDNString(
OID::COMMON_NAME,
'Hello World'
));
// X509
printVariableInfo(new CertificateSubject(
'Friedrich Grosse',
'friedrich.grosse@gmail.com',
'PHPASN1',
'Berlin',
'Berlin',
'DE',
'Development Department'
));
printVariableInfo(new PublicKey(
'00 00 00 01 23 00 00 01 01 00 E7 40 F2 69 15 D5 4A DD 24 A1 C3
0C 93 38 03 1A 49 70 EB 76 B0 F5 1B BF 42 97 BA E1 F3 61 CC E1
63 0A BA 0F 66 6D 30 4E 31 67 65 39 E1 2E 93 A7 E7 3E 1E 76 10
81 5A 41 C6 DF 12 05 46 AC 61 A9 29 A3 BD 1A 16 35 38 DF 75 F6
0A 25 F7 3A F5 D3 C6 EB 84 7D 40 D4 1E 56 CA 0B 46 28 F7 79 D6
08 BB 1B 01 C7 2A 14 93 B2 F7 89 CB 11 F3 4B E6 5F 7A 2B 22 F4
14 CB 36 47 40 89 45 FC 14 03 1D 8B 31 AF 4C 57 63 7D 87 FD 95
0C ED 64 C1 BF 8F FC E2 76 6C 90 A3 4F 50 72 9D AB 01 1E 84 7A
63 95 9A 2E 0E 59 F1 AF A9 D0 6E AB 8E 45 96 80 AD 57 26 04 2F
B7 B0 41 69 2B 7A BD F9 2F FE 60 99 A9 81 FF 94 A5 8E 5B 7F A8
C0 63 4B F3 79 F4 F7 3C 27 81 FF 90 5C C6 04 BC C2 C1 62 2E 47
8A EF 6F 95 53 89 99 34 7C 4C B4 AE A0 E8 AD C1 04 B8 B6 E2 02
BB 65 93 39 42 F9 FD 69 20 E3 CF EF 49 E9'
));
printVariableInfo(new AlgorithmIdentifier(OID::SHA1_WITH_RSA_SIGNATURE));
printVariableInfo($csr = new CSR(
'Friedrich Grosse',
'friedrich.grosse@gmail.com',
'PHPASN1',
'Berlin',
'Berlin',
'DE',
'Development Department',
'3082010a02820101009e2a7defae93720c91c43c46ff4a1f2e8eef7949289e281f788f3a07d9b94da26fb2e721009caceddd0e6b59daa596df20f871fc30a43f4b80798f94fa3d13cb2db79eb6d8f07b4065d0b09a541564ba3baa1201e20ee923ea16be31fa785c300635c4e881df7acb5b52c7c3d923067902cc55e77c00694f319d2b9e81edbbfe70ef1a462aef4960c567f33aa5264a05fdf24cd7bc36941cd7746fb767631a241b7a97fc4cdc42a68692b906406403599380c7586ce6f22fac34949caf1072c724ba5397e6440f957e2678c3a4bc92268fe6815d41fa210ab45364c11e3731c6c039832b54f54b51fdaf6afb351e1da9720b3c322f7fbaefb72d96d4ce5ec07b0203010001',
'4adf270d347047192573cf245a94cd2e69594c1cdac1d7c99d7ed5856c926ee62c65188f21d893e634b213595cc4564d5a8d39bed0ca01e0b45e3182ab89310c129017f2a7a68d8603694ddc8d1c2ebfee39b3b5dfc9dbc2db667a089b1b51386f2cf7ec70140d185bae5c2f3b3148b9ef613ce068f94db13a230b1133e4b4a48ec5c8b4066d64a2199c0cfb6c4d0cfe105f21a89b2900d0a5c87bef5eded941ba93ae1b7e84aaeabcb46fa4a3844ffc683ebb4ee80717ff51cba5d82afe9d2633b760a66449e57e06d73eeeb151bc050a66825996d7f5ec821d31891c620a677c8271db13bbc22fcf91e1b7ac8f6f109eb8e3a2c61a3c8a4336b40a499e1404'
));
?>
</table>
</body>
</html>

View file

@ -0,0 +1,38 @@
table {
border-collapse: collapse;
}
th {
text-align: left;
}
td {
border-top: black 1px solid;
padding-right: 30px;
}
td.ASNclass {
font-weight: bold;
width: 150px;
}
td.openSSL {
border-left: black 1px solid;
}
p.notice {
margin-bottom: 30px;
color: gray;
}
.red {
color: red;
}
.monospace {
font-family: monospace;
}
pre {
margin: 7px
}

View file

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require_once __DIR__.'/../vendor/autoload.php';
use FG\ASN1\OID;
function echoOIDRow($oidString)
{
$oidName = OID::getName($oidString);
echo "<tr><td>{$oidString}</td><td>{$oidName}</td></tr>";
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PHPASN1 Examples</title>
<meta name="description" content="Howto get the name of object identifiers with PHPASN1">
<meta name="author" content="Friedrich Große">
<style type="text/css">td {padding: 0 10px;}</style>
</head>
<body>
<table border=1>
<?php
echoOIDRow('1.2.840.113549.1.1.1');
echoOIDRow('1.2.840.113549.1.1.5');
echoOIDRow('2.5.29.37');
?>
</table>
</body>
</html>

View file

@ -0,0 +1,87 @@
<?php
/**
* View any string as a hexdump.
*
* DISCLOSURE: This script has been taken from http://aidanlister.com/2004/04/viewing-binary-data-as-a-hexdump-in-php
*
* This is most commonly used to view binary data from streams
* or sockets while debugging, but can be used to view any string
* with non-viewable characters.
*
* @version 1.3.2
*
* @author Aidan Lister <aidan@php.net>
* @author Peter Waller <iridum@php.net>
*
* @link http://aidanlister.com/2004/04/viewing-binary-data-as-a-hexdump-in-php/
*
* @param string $data The string to be dumped
* @param bool $htmloutput Set to false for non-HTML output
* @param bool $uppercase Set to true for uppercase hex
* @param bool $return Set to true to return the dump
*
* @return string
*/
function hexdump($data, $htmloutput = true, $uppercase = false, $return = false)
{
// Init
$hexi = '';
$ascii = '';
$dump = ($htmloutput === true) ? '<pre>' : '';
$offset = 0;
$len = strlen($data);
// Upper or lower case hexadecimal
$x = ($uppercase === false) ? 'x' : 'X';
// Iterate string
for ($i = $j = 0; $i < $len; $i++) {
// Convert to hexidecimal
$hexi .= sprintf("%02$x ", ord($data[$i]));
// Replace non-viewable bytes with '.'
if (ord($data[$i]) >= 32) {
$ascii .= ($htmloutput === true) ?
htmlentities($data[$i]) :
$data[$i];
} else {
$ascii .= '.';
}
// Add extra column spacing
if ($j === 7) {
$hexi .= ' ';
$ascii .= ' ';
}
// Add row
if (++$j === 16 || $i === $len - 1) {
// Join the hexi / ascii output
$dump .= sprintf("%04$x %-49s %s", $offset, $hexi, $ascii);
// Reset vars
$hexi = $ascii = '';
$offset += 16;
$j = 0;
// Add newline
if ($i !== $len - 1) {
$dump .= "\n";
}
}
}
// Finish dump
$dump .= $htmloutput === true ?
'</pre>' :
'';
$dump .= "\n";
// Output method
if ($return === false) {
echo $dump;
} else {
return $dump;
}
return null;
}

View file

@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PHPASN1 Examples</title>
<meta name="description" content="Examples for the PHPASN1 API">
<meta name="author" content="Friedrich Große">
</head>
<body>
Currently the following examples are available:
<ul>
<li><a href="allClasses.php">All available ASN1 classes in action</a></li>
<li><a href="parseBinary.php">Howto parse binary data</a></li>
<li><a href="getObjectIdentifier.php">Howto get the name of object identifers</a></li>
</ul>
</body>
</html>

View file

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require_once __DIR__.'/../vendor/autoload.php';
require_once 'shared.php';
use FG\ASN1\Object;
$base64String =
'MIIFGDCCAwACAQAwOjEWMBQGCgmSJomT8ixkARkWBnNlY3VyZTEgMB4GA1UEAxMX
LlNlY3VyZSBFbnRlcnByaXNlIENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQCzgEpL+Za7a3y7YpURDrxlGIBlks25fD0tHaZIYkBTaXA5h+9MWoXn
FA7AlIUt8pbBvXdJbOCmGaeQmBfBH0Qy9vTbx/DR2IOwzqy2ZHuurI5bPL12ceE2
Mxa9xgY/i7U6MAUtoA3amEd7cKj2fz9EWZruRladOX0DXv9KexSan+45QjCWH+u2
Cxem2zH9ZDNPGBuAF9YsAvkdHdAoX8aSm05ZAjUiO2e/+L57whh7zZiDY3WIhin7
N/2JNTKVO6lx50S8a34XUKBt3SKgSR941hcLrBYUNftUYsTPo40bzKKcWqemiH+w
jQiDrln4V2b5EbVeoGWe4UDPXCVmC6UPklG7iYfF0eeK4ujV8uc9PtV2LvGLOFdm
AYE3+FAba5byQATw/DY8EJKQ7ptPigJhVe47NNeJlsKwk1haJ9k8ZazjS+vT45B5
pqe0yBFAEon8TFnOLnAOblmKO12i0zqMUNAAlmr1c8jNjLr+dhruS+QropZmzZ24
mAnFG+Y0qpfhMzAxTGQyVjyGwDfRK/ARmtrGpmROjj5+6VuMmZ6Ljf3xN09epmtH
gJe+lYNBlpfUYg16tm+OusnziYnXL6nIo2ChOY/7GNJJif9fjvvaPDCC98K64av5
5rpIx7N/XH4hwHeQQkEQangExE+8UMyBNFNmvPnIHVHUZdYo4SLsYwIDAQABoIGY
MBsGCisGAQQBgjcNAgMxDRYLNi4zLjk2MDAuMi4weQYJKoZIhvcNAQkOMWwwajAQ
BgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU5nEIMEUT5mMd1WepmviwgK7dIzww
GQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAKZl6bAeaID3b/ic4aztL8ZZI7vi
D3A9otUKx6v1Xe63zDPR+DiWSnxb9m+l8OPtnWkcLkzEIM/IMWorHKUAJ/J871D0
Qx+0/HbkcrjMtVu/dNrtb9Z9CXup66ZvxTPcpEziq0/n2yw8QdBaa+lli65Qcwcy
tzMQK6WQTRYfvVCIX9AKcPKxwx1DLH+7hL/bERB1lUDu59Jx6fQfqJrFVOY2N8c0
MGvurfoHGmEoyCMIyvmIMu4+/wSNEE/sSDp4lZ6zuF6rf1m0GiLdTX2XJE+gfvep
JTFmp4S3WFqkszKvaxBIT+jV0XKTNDwnO+dpExwU4jZUh18CdEFkIUuQb0gFF8B7
WJFVpNdsRqZRPBz83BW1Kjo0yAmaoTrGNmG0p6Qf3K2zbk1+Jik3VZq4rvKoTi20
6RvLA2//cMNfkYPsuqvoHGe2e0GOLtIB63wJzloWROpb72ohEHsvCKullIJVSuiS
9sfTBAenHCyndgAEd4T3npTUdaiNumVEm5ilZId7LAYekJhkgFu3vlcl8blBJKjE
skVTp7JpBmdXCL/G/6H2SFjca4JMOAy3DxwlGdgneIaXazHs5nBK/BgKPIyPzZ4w
secxBTTCNgI48YezK3GDkn65cmlnkt6F6Mf0MwoDaXTuB88Jycbwb5ihKnHEJIsO
draiRBZruwMPwPIP';
$binaryData = base64_decode($base64String);
$asnObject = Object::fromBinary($binaryData);
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PHPASN1 Examples</title>
<meta name="description" content="How to parse binary ASN.1 data with PHPASN1">
<meta name="author" content="Friedrich Große">
</head>
<body>
<p>Got <?= strlen($binaryData) ?> byte of binary data: </p>
<pre><?php printObject($asnObject);?></pre>
</body>
</html>

View file

@ -0,0 +1,32 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use FG\ASN1\OID;
use FG\ASN1\Universal\Integer;
use FG\ASN1\Universal\Boolean;
use FG\ASN1\Universal\Enumerated;
use FG\ASN1\Universal\IA5String;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\PrintableString;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\Set;
use FG\ASN1\Universal\NullObject;
$integer = new Integer(123456);
$boolean = new Boolean(true);
$enum = new Enumerated(1);
$ia5String = new IA5String('Hello world');
$asnNull = new NullObject();
$objectIdentifier1 = new ObjectIdentifier('1.2.250.1.16.9');
$objectIdentifier2 = new ObjectIdentifier(OID::RSA_ENCRYPTION);
$printableString = new PrintableString('Foo bar');
$sequence = new Sequence($integer, $boolean, $enum, $ia5String);
$set = new Set($sequence, $asnNull, $objectIdentifier1, $objectIdentifier2, $printableString);
$myBinary = $sequence->getBinary();
$myBinary .= $set->getBinary();
echo base64_encode($myBinary);

View file

@ -0,0 +1,25 @@
<?php
use FG\ASN1\Identifier;
use FG\ASN1\Object;
function printObject(Object $object, $depth = 0)
{
$treeSymbol = '';
$depthString = str_repeat('─', $depth);
if ($depth > 0) {
$treeSymbol = '├';
}
$name = Identifier::getShortName($object->getType());
echo "{$treeSymbol}{$depthString}{$name} : ";
echo $object->__toString().PHP_EOL;
$content = $object->getContent();
if (is_array($content)) {
foreach ($object as $child) {
printObject($child, $depth + 1);
}
}
}

View file

@ -0,0 +1,136 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
use Exception;
abstract class AbstractString extends Object implements Parsable
{
/** @var string */
protected $value;
private $checkStringForIllegalChars = true;
private $allowedCharacters = [];
/**
* The abstract base class for ASN.1 classes which represent some string of character.
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
}
public function getContent()
{
return $this->value;
}
protected function allowCharacter($character)
{
$this->allowedCharacters[] = $character;
}
protected function allowCharacters(...$characters)
{
foreach ($characters as $character) {
$this->allowedCharacters[] = $character;
}
}
protected function allowNumbers()
{
foreach (range('0', '9') as $char) {
$this->allowedCharacters[] = (string) $char;
}
}
protected function allowAllLetters()
{
$this->allowSmallLetters();
$this->allowCapitalLetters();
}
protected function allowSmallLetters()
{
foreach (range('a', 'z') as $char) {
$this->allowedCharacters[] = $char;
}
}
protected function allowCapitalLetters()
{
foreach (range('A', 'Z') as $char) {
$this->allowedCharacters[] = $char;
}
}
protected function allowSpaces()
{
$this->allowedCharacters[] = ' ';
}
protected function allowAll()
{
$this->checkStringForIllegalChars = false;
}
protected function calculateContentLength()
{
return strlen($this->value);
}
protected function getEncodedValue()
{
if ($this->checkStringForIllegalChars) {
$this->checkString();
}
return $this->value;
}
protected function checkString()
{
$stringLength = $this->getContentLength();
for ($i = 0; $i < $stringLength; $i++) {
if (in_array($this->value[$i], $this->allowedCharacters) == false) {
$typeName = Identifier::getName($this->getType());
throw new Exception("Could not create a {$typeName} from the character sequence '{$this->value}'.");
}
}
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
$parsedObject = new static('');
self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
$string = substr($binaryData, $offsetIndex, $contentLength);
$offsetIndex += $contentLength;
$parsedObject->value = $string;
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
public static function isValid($string)
{
$testObject = new static($string);
try {
$testObject->checkString();
return true;
} catch (Exception $exception) {
return false;
}
}
}

View file

@ -0,0 +1,78 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
use DateInterval;
use DateTime;
use DateTimeZone;
use Exception;
abstract class AbstractTime extends Object
{
/** @var DateTime */
protected $value;
public function __construct($dateTime = null, $dateTimeZone = 'UTC')
{
if ($dateTime == null || is_string($dateTime)) {
$timeZone = new DateTimeZone($dateTimeZone);
$dateTimeObject = new DateTime($dateTime, $timeZone);
if ($dateTimeObject == false) {
$errorMessage = $this->getLastDateTimeErrors();
$className = Identifier::getName($this->getType());
throw new Exception(sprintf("Could not create %s from date time string '%s': %s", $className, $dateTime, $errorMessage));
}
$dateTime = $dateTimeObject;
} elseif (!$dateTime instanceof DateTime) {
throw new Exception('Invalid first argument for some instance of ASN_AbstractTime constructor');
}
$this->value = $dateTime;
}
public function getContent()
{
return $this->value;
}
protected function getLastDateTimeErrors()
{
$messages = '';
$lastErrors = DateTime::getLastErrors();
foreach ($lastErrors['errors'] as $errorMessage) {
$messages .= "{$errorMessage}, ";
}
return substr($messages, 0, -2);
}
public function __toString()
{
return $this->value->format("Y-m-d\tH:i:s");
}
protected static function extractTimeZoneData(&$binaryData, &$offsetIndex, DateTime $dateTime)
{
$sign = $binaryData[$offsetIndex++];
$timeOffsetHours = intval(substr($binaryData, $offsetIndex, 2));
$timeOffsetMinutes = intval(substr($binaryData, $offsetIndex + 2, 2));
$offsetIndex += 4;
$interval = new DateInterval("PT{$timeOffsetHours}H{$timeOffsetMinutes}M");
if ($sign == '+') {
$dateTime->sub($interval);
} else {
$dateTime->add($interval);
}
return $dateTime;
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace FG\ASN1;
use InvalidArgumentException;
/**
* A base-128 decoder.
*/
class Base128
{
/**
* @param int $value
*
* @return string
*/
public static function encode($value)
{
$value = gmp_init($value, 10);
$octets = chr(gmp_strval(gmp_and($value, 0x7f), 10));
$rightShift = function ($number, $positions) {
return gmp_div($number, gmp_pow(2, $positions));
};
$value = $rightShift($value, 7);
while (gmp_cmp($value, 0) > 0) {
$octets .= chr(gmp_strval(gmp_or(0x80, gmp_and($value, 0x7f)), 10));
$value = $rightShift($value, 7);
}
return strrev($octets);
}
/**
* @param string $octets
*
* @throws InvalidArgumentException if the given octets represent a malformed base-128 value or the decoded value would exceed the the maximum integer length
*
* @return int
*/
public static function decode($octets)
{
$bitsPerOctet = 7;
$value = gmp_init(0, 10);
$i = 0;
$leftShift = function ($number, $positions) {
return gmp_mul($number, gmp_pow(2, $positions));
};
while (true) {
if (!isset($octets[$i])) {
throw new InvalidArgumentException(sprintf('Malformed base-128 encoded value (0x%s).', strtoupper(bin2hex($octets)) ?: '0'));
}
$octet = gmp_init(ord($octets[$i++]), 10);
$l1 = $leftShift($value, $bitsPerOctet);
$r1 = gmp_and($octet, 0x7f);
$value = gmp_add($l1, $r1);
if (0 === gmp_cmp(gmp_and($octet, 0x80), 0)) {
break;
}
}
return gmp_strval($value);
}
}

View file

@ -0,0 +1,35 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Composite;
use FG\ASN1\Object;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\ObjectIdentifier;
class AttributeTypeAndValue extends Sequence
{
/**
* @param ObjectIdentifier|string $objIdentifier
* @param \FG\ASN1\Object $value
*/
public function __construct($objIdentifier, Object $value)
{
if ($objIdentifier instanceof ObjectIdentifier == false) {
$objIdentifier = new ObjectIdentifier($objIdentifier);
}
parent::__construct($objIdentifier, $value);
}
public function __toString()
{
return $this->children[0].': '.$this->children[1];
}
}

View file

@ -0,0 +1,37 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Composite;
use FG\ASN1\Universal\PrintableString;
use FG\ASN1\Universal\IA5String;
use FG\ASN1\Universal\UTF8String;
class RDNString extends RelativeDistinguishedName
{
/**
* @param string|\FG\ASN1\Universal\ObjectIdentifier $objectIdentifierString
* @param string|\FG\ASN1\Object $value
*/
public function __construct($objectIdentifierString, $value)
{
if (PrintableString::isValid($value)) {
$value = new PrintableString($value);
} else {
if (IA5String::isValid($value)) {
$value = new IA5String($value);
} else {
$value = new UTF8String($value);
}
}
parent::__construct($objectIdentifierString, $value);
}
}

View file

@ -0,0 +1,50 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Composite;
use FG\ASN1\Exception\NotImplementedException;
use FG\ASN1\Object;
use FG\ASN1\Universal\Set;
class RelativeDistinguishedName extends Set
{
/**
* @param string|\FG\ASN1\Universal\ObjectIdentifier $objIdentifierString
* @param \FG\ASN1\Object $value
*/
public function __construct($objIdentifierString, Object $value)
{
// TODO: This does only support one element in the RelativeDistinguishedName Set but it it is defined as follows:
// RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
parent::__construct(new AttributeTypeAndValue($objIdentifierString, $value));
}
public function getContent()
{
/** @var \FG\ASN1\Object $firstObject */
$firstObject = $this->children[0];
return $firstObject->__toString();
}
/**
* At the current version this code can not work since the implementation of Construct requires
* the class to support a constructor without arguments.
*
* @deprecated this function is not yet implemented! Feel free to submit a pull request on github
* @param string $binaryData
* @param int $offsetIndex
* @throws NotImplementedException
*/
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
throw new NotImplementedException();
}
}

View file

@ -0,0 +1,185 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
use ArrayAccess;
use ArrayIterator;
use Countable;
use Iterator;
abstract class Construct extends Object implements Countable, ArrayAccess, Iterator, Parsable
{
/** @var \FG\ASN1\Object[] */
protected $children;
private $iteratorPosition;
/**
* @param \FG\ASN1\Object[] $children the variadic type hint is commented due to https://github.com/facebook/hhvm/issues/4858
*/
public function __construct(/* HH_FIXME[4858]: variadic + strict */ ...$children)
{
$this->children = $children;
$this->iteratorPosition = 0;
}
public function getContent()
{
return $this->children;
}
public function rewind()
{
$this->iteratorPosition = 0;
}
public function current()
{
return $this->children[$this->iteratorPosition];
}
public function key()
{
return $this->iteratorPosition;
}
public function next()
{
$this->iteratorPosition++;
}
public function valid()
{
return isset($this->children[$this->iteratorPosition]);
}
public function offsetExists($offset)
{
return array_key_exists($offset, $this->children);
}
public function offsetGet($offset)
{
return $this->children[$offset];
}
public function offsetSet($offset, $value)
{
if ($offset === null) {
$offset = count($this->children);
}
$this->children[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->children[$offset]);
}
protected function calculateContentLength()
{
$length = 0;
foreach ($this->children as $component) {
$length += $component->getObjectLength();
}
return $length;
}
protected function getEncodedValue()
{
$result = '';
foreach ($this->children as $component) {
$result .= $component->getBinary();
}
return $result;
}
public function addChild(Object $child)
{
$this->children[] = $child;
}
public function addChildren(array $children)
{
foreach ($children as $child) {
$this->addChild($child);
}
}
public function __toString()
{
$nrOfChildren = $this->getNumberOfChildren();
$childString = $nrOfChildren == 1 ? 'child' : 'children';
return "[{$nrOfChildren} {$childString}]";
}
public function getNumberOfChildren()
{
return count($this->children);
}
/**
* @return \FG\ASN1\Object[]
*/
public function getChildren()
{
return $this->children;
}
/**
* @return \FG\ASN1\Object
*/
public function getFirstChild()
{
return $this->children[0];
}
/**
* @param string $binaryData
* @param int $offsetIndex
*
* @throws Exception\ParserException
*
* @return Construct|static
*/
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
$parsedObject = new static();
self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
$children = [];
$octetsToRead = $contentLength;
while ($octetsToRead > 0) {
$newChild = Object::fromBinary($binaryData, $offsetIndex);
$octetsToRead -= $newChild->getObjectLength();
$children[] = $newChild;
}
$parsedObject->addChildren($children);
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
public function count($mode = COUNT_NORMAL)
{
return count($this->children, $mode);
}
public function getIterator()
{
return new ArrayIterator($this->children);
}
}

View file

@ -0,0 +1,15 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Exception;
class NotImplementedException extends \Exception
{
}

View file

@ -0,0 +1,29 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Exception;
class ParserException extends \Exception
{
private $errorMessage;
private $offset;
public function __construct($errorMessage, $offset)
{
$this->errorMessage = $errorMessage;
$this->offset = $offset;
parent::__construct("ASN.1 Parser Exception at offset {$this->offset}: {$this->errorMessage}");
}
public function getOffset()
{
return $this->offset;
}
}

View file

@ -0,0 +1,131 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
use FG\ASN1\Exception\ParserException;
/**
* Class ExplicitlyTaggedObject decorate an inner object with an additional tag that gives information about
* its context specific meaning.
*
* Explanation taken from A Layman's Guide to a Subset of ASN.1, BER, and DER:
* >>> An RSA Laboratories Technical Note
* >>> Burton S. Kaliski Jr.
* >>> Revised November 1, 1993
*
* [...]
* Explicitly tagged types are derived from other types by adding an outer tag to the underlying type.
* In effect, explicitly tagged types are structured types consisting of one component, the underlying type.
* Explicit tagging is denoted by the ASN.1 keywords [class number] EXPLICIT (see Section 5.2).
* [...]
*
* @see http://luca.ntop.org/Teaching/Appunti/asn1.html
*/
class ExplicitlyTaggedObject extends Object
{
/** @var \FG\ASN1\Object[] */
private $decoratedObjects;
private $tag;
/**
* @param int $tag
* @param \FG\ASN1\Object $objects,...
*/
public function __construct($tag, /* HH_FIXME[4858]: variadic + strict */ ...$objects)
{
$this->tag = $tag;
$this->decoratedObjects = $objects;
}
protected function calculateContentLength()
{
$length = 0;
foreach ($this->decoratedObjects as $object) {
$length += $object->getObjectLength();
}
return $length;
}
protected function getEncodedValue()
{
$encoded = '';
foreach ($this->decoratedObjects as $object) {
$encoded .= $object->getBinary();
}
return $encoded;
}
public function getContent()
{
return $this->decoratedObjects;
}
public function __toString()
{
switch ($length = count($this->decoratedObjects)) {
case 0:
return "Context specific empty object with tag [{$this->tag}]";
case 1:
$decoratedType = Identifier::getShortName($this->decoratedObjects[0]->getType());
return "Context specific $decoratedType with tag [{$this->tag}]";
default:
return "$length context specific objects with tag [{$this->tag}]";
}
}
public function getType()
{
return ord($this->getIdentifier());
}
public function getIdentifier()
{
$identifier = Identifier::create(Identifier::CLASS_CONTEXT_SPECIFIC, true, $this->tag);
return is_int($identifier) ? chr($identifier) : $identifier;
}
public function getTag()
{
return $this->tag;
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
$identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex);
$firstIdentifierOctet = ord($identifier);
assert(Identifier::isContextSpecificClass($firstIdentifierOctet), 'identifier octet should indicate context specific class');
assert(Identifier::isConstructed($firstIdentifierOctet), 'identifier octet should indicate constructed object');
$tag = Identifier::getTagNumber($identifier);
$totalContentLength = self::parseContentLength($binaryData, $offsetIndex);
$remainingContentLength = $totalContentLength;
$offsetIndexOfDecoratedObject = $offsetIndex;
$decoratedObjects = [];
while ($remainingContentLength > 0) {
$nextObject = Object::fromBinary($binaryData, $offsetIndex);
$remainingContentLength -= $nextObject->getObjectLength();
$decoratedObjects[] = $nextObject;
}
if ($remainingContentLength != 0) {
throw new ParserException("Context-Specific explicitly tagged object [$tag] starting at offset $offsetIndexOfDecoratedObject specifies a length of $totalContentLength octets but $remainingContentLength remain after parsing the content", $offsetIndexOfDecoratedObject);
}
$parsedObject = new self($tag, ...$decoratedObjects);
$parsedObject->setContentLength($totalContentLength);
return $parsedObject;
}
}

View file

@ -0,0 +1,339 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
use Exception;
/**
* The Identifier encodes the ASN.1 tag (class and number) of the type of a data value.
*
* Every identifier whose number is in the range 0 to 30 has the following structure:
*
* Bits: 8 7 6 5 4 3 2 1
* | Class | P/C | Tag number |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Bits 8 and 7 define the class of this type ( Universal, Application, Context-specific or Private).
* Bit 6 encoded whether this type is primitive or constructed
* The remaining bits 5 - 1 encode the tag number
*/
class Identifier
{
const CLASS_UNIVERSAL = 0x00;
const CLASS_APPLICATION = 0x01;
const CLASS_CONTEXT_SPECIFIC = 0x02;
const CLASS_PRIVATE = 0x03;
const EOC = 0x00; // unsupported for now
const BOOLEAN = 0x01;
const INTEGER = 0x02;
const BITSTRING = 0x03;
const OCTETSTRING = 0x04;
const NULL = 0x05;
const OBJECT_IDENTIFIER = 0x06;
const OBJECT_DESCRIPTOR = 0x07;
const EXTERNAL = 0x08; // unsupported for now
const REAL = 0x09; // unsupported for now
const ENUMERATED = 0x0A;
const EMBEDDED_PDV = 0x0B; // unsupported for now
const UTF8_STRING = 0x0C;
const RELATIVE_OID = 0x0D;
// value 0x0E and 0x0F are reserved for future use
const SEQUENCE = 0x30;
const SET = 0x31;
const NUMERIC_STRING = 0x12;
const PRINTABLE_STRING = 0x13;
const T61_STRING = 0x14; // sometimes referred to as TeletextString
const VIDEOTEXT_STRING = 0x15;
const IA5_STRING = 0x16;
const UTC_TIME = 0x17;
const GENERALIZED_TIME = 0x18;
const GRAPHIC_STRING = 0x19;
const VISIBLE_STRING = 0x1A;
const GENERAL_STRING = 0x1B;
const UNIVERSAL_STRING = 0x1C;
const CHARACTER_STRING = 0x1D; // Unrestricted character type
const BMP_STRING = 0x1E;
const LONG_FORM = 0x1F;
const IS_CONSTRUCTED = 0x20;
/**
* Creates an identifier. Short form identifiers are returned as integers
* for BC, long form identifiers will be returned as a string of octets.
*
* @param int $class
* @param bool $isConstructed
* @param int $tagNumber
*
* @throws Exception if the given arguments are invalid
*
* @return int|string
*/
public static function create($class, $isConstructed, $tagNumber)
{
if (!is_numeric($class) || $class < self::CLASS_UNIVERSAL || $class > self::CLASS_PRIVATE) {
throw new Exception(sprintf('Invalid class %d given', $class));
}
if (!is_bool($isConstructed)) {
throw new Exception("\$isConstructed must be a boolean value ($isConstructed given)");
}
$tagNumber = self::makeNumeric($tagNumber);
if ($tagNumber < 0) {
throw new Exception(sprintf('Invalid $tagNumber %d given. You can only use positive integers.', $tagNumber));
}
if ($tagNumber < self::LONG_FORM) {
return ($class << 6) | ($isConstructed << 5) | $tagNumber;
}
$firstOctet = ($class << 6) | ($isConstructed << 5) | self::LONG_FORM;
// Tag numbers formatted in long form are base-128 encoded. See X.609#8.1.2.4
return chr($firstOctet).Base128::encode($tagNumber);
}
public static function isConstructed($identifierOctet)
{
return ($identifierOctet & self::IS_CONSTRUCTED) === self::IS_CONSTRUCTED;
}
public static function isLongForm($identifierOctet)
{
return ($identifierOctet & self::LONG_FORM) === self::LONG_FORM;
}
/**
* Return the name of the mapped ASN.1 type with a preceding "ASN.1 ".
*
* Example: ASN.1 Octet String
*
* @see Identifier::getShortName()
*
* @param int|string $identifier
*
* @return string
*/
public static function getName($identifier)
{
$identifierOctet = self::makeNumeric($identifier);
$typeName = static::getShortName($identifier);
if (($identifierOctet & self::LONG_FORM) < self::LONG_FORM) {
$typeName = "ASN.1 {$typeName}";
}
return $typeName;
}
/**
* Return the short version of the type name.
*
* If the given identifier octet can be mapped to a known universal type this will
* return its name. Else Identifier::getClassDescription() is used to retrieve
* information about the identifier.
*
* @see Identifier::getName()
* @see Identifier::getClassDescription()
*
* @param int|string $identifier
*
* @return string
*/
public static function getShortName($identifier)
{
$identifierOctet = self::makeNumeric($identifier);
switch ($identifierOctet) {
case self::EOC:
return 'End-of-contents octet';
case self::BOOLEAN:
return 'Boolean';
case self::INTEGER:
return 'Integer';
case self::BITSTRING:
return 'Bit String';
case self::OCTETSTRING:
return 'Octet String';
case self::NULL:
return 'NULL';
case self::OBJECT_IDENTIFIER:
return 'Object Identifier';
case self::OBJECT_DESCRIPTOR:
return 'Object Descriptor';
case self::EXTERNAL:
return 'External Type';
case self::REAL:
return 'Real';
case self::ENUMERATED:
return 'Enumerated';
case self::EMBEDDED_PDV:
return 'Embedded PDV';
case self::UTF8_STRING:
return 'UTF8 String';
case self::RELATIVE_OID:
return 'Relative OID';
case self::SEQUENCE:
return 'Sequence';
case self::SET:
return 'Set';
case self::NUMERIC_STRING:
return 'Numeric String';
case self::PRINTABLE_STRING:
return 'Printable String';
case self::T61_STRING:
return 'T61 String';
case self::VIDEOTEXT_STRING:
return 'Videotext String';
case self::IA5_STRING:
return 'IA5 String';
case self::UTC_TIME:
return 'UTC Time';
case self::GENERALIZED_TIME:
return 'Generalized Time';
case self::GRAPHIC_STRING:
return 'Graphic String';
case self::VISIBLE_STRING:
return 'Visible String';
case self::GENERAL_STRING:
return 'General String';
case self::UNIVERSAL_STRING:
return 'Universal String';
case self::CHARACTER_STRING:
return 'Character String';
case self::BMP_STRING:
return 'BMP String';
case 0x0E:
return 'RESERVED (0x0E)';
case 0x0F:
return 'RESERVED (0x0F)';
case self::LONG_FORM:
default:
$classDescription = self::getClassDescription($identifier);
if (is_int($identifier)) {
$identifier = chr($identifier);
}
return "$classDescription (0x".strtoupper(bin2hex($identifier)).')';
}
}
/**
* Returns a textual description of the information encoded in a given identifier octet.
*
* The first three (most significant) bytes are evaluated to determine if this is a
* constructed or primitive type and if it is either universal, application, context-specific or
* private.
*
* Example:
* Constructed context-specific
* Primitive universal
*
* @param int|string $identifier
*
* @return string
*/
public static function getClassDescription($identifier)
{
$identifierOctet = self::makeNumeric($identifier);
if (self::isConstructed($identifierOctet)) {
$classDescription = 'Constructed ';
} else {
$classDescription = 'Primitive ';
}
$classBits = $identifierOctet >> 6;
switch ($classBits) {
case self::CLASS_UNIVERSAL:
$classDescription .= 'universal';
break;
case self::CLASS_APPLICATION:
$classDescription .= 'application';
break;
case self::CLASS_CONTEXT_SPECIFIC:
$tagNumber = self::getTagNumber($identifier);
$classDescription = "[$tagNumber] Context-specific";
break;
case self::CLASS_PRIVATE:
$classDescription .= 'private';
break;
default:
return "INVALID IDENTIFIER OCTET: {$identifierOctet}";
}
return $classDescription;
}
/**
* @param int|string $identifier
*
* @return int
*/
public static function getTagNumber($identifier)
{
$firstOctet = self::makeNumeric($identifier);
$tagNumber = $firstOctet & self::LONG_FORM;
if ($tagNumber < self::LONG_FORM) {
return $tagNumber;
}
if (is_numeric($identifier)) {
$identifier = chr($identifier);
}
return Base128::decode(substr($identifier, 1));
}
public static function isUniversalClass($identifier)
{
$identifier = self::makeNumeric($identifier);
return $identifier >> 6 == self::CLASS_UNIVERSAL;
}
public static function isApplicationClass($identifier)
{
$identifier = self::makeNumeric($identifier);
return $identifier >> 6 == self::CLASS_APPLICATION;
}
public static function isContextSpecificClass($identifier)
{
$identifier = self::makeNumeric($identifier);
return $identifier >> 6 == self::CLASS_CONTEXT_SPECIFIC;
}
public static function isPrivateClass($identifier)
{
$identifier = self::makeNumeric($identifier);
return $identifier >> 6 == self::CLASS_PRIVATE;
}
private static function makeNumeric($identifierOctet)
{
if (!is_numeric($identifierOctet)) {
return ord($identifierOctet);
} else {
return $identifierOctet;
}
}
}

View file

@ -0,0 +1,198 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
class OID
{
const RSA_ENCRYPTION = '1.2.840.113549.1.1.1';
const MD5_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.4';
const SHA1_WITH_RSA_SIGNATURE = '1.2.840.113549.1.1.5';
const PKCS9_EMAIL = '1.2.840.113549.1.9.1';
const PKCS9_UNSTRUCTURED_NAME = '1.2.840.113549.1.9.2';
const PKCS9_CONTENT_TYPE = '1.2.840.113549.1.9.3';
const PKCS9_MESSAGE_DIGEST = '1.2.840.113549.1.9.4';
const PKCS9_SIGNING_TIME = '1.2.840.113549.1.9.5';
const PKCS9_EXTENSION_REQUEST = '1.2.840.113549.1.9.14';
// certificate extension identifier
const CERT_EXT_SUBJECT_DIRECTORY_ATTR = '2.5.29.9';
const CERT_EXT_SUBJECT_KEY_IDENTIFIER = '2.5.29.14';
const CERT_EXT_KEY_USAGE = '2.5.29.15';
const CERT_EXT_PRIVATE_KEY_USAGE_PERIOD = '2.5.29.16';
const CERT_EXT_SUBJECT_ALT_NAME = '2.5.29.17';
const CERT_EXT_ISSUER_ALT_NAME = '2.5.29.18';
const CERT_EXT_BASIC_CONSTRAINTS = '2.5.29.19';
const CERT_EXT_CRL_NUMBER = '2.5.29.20';
const CERT_EXT_REASON_CODE = '2.5.29.21';
const CERT_EXT_INVALIDITY_DATE = '2.5.29.24';
const CERT_EXT_DELTA_CRL_INDICATOR = '2.5.29.27';
const CERT_EXT_ISSUING_DIST_POINT = '2.5.29.28';
const CERT_EXT_CERT_ISSUER = '2.5.29.29';
const CERT_EXT_NAME_CONSTRAINTS = '2.5.29.30';
const CERT_EXT_CRL_DISTRIBUTION_POINTS = '2.5.29.31';
const CERT_EXT_CERT_POLICIES = '2.5.29.32';
const CERT_EXT_AUTHORITY_KEY_IDENTIFIER = '2.5.29.35';
const CERT_EXT_EXTENDED_KEY_USAGE = '2.5.29.37';
// standard certificate files
const COMMON_NAME = '2.5.4.3';
const SURNAME = '2.5.4.4';
const SERIAL_NUMBER = '2.5.4.5';
const COUNTRY_NAME = '2.5.4.6';
const LOCALITY_NAME = '2.5.4.7';
const STATE_OR_PROVINCE_NAME = '2.5.4.8';
const STREET_ADDRESS = '2.5.4.9';
const ORGANIZATION_NAME = '2.5.4.10';
const OU_NAME = '2.5.4.11';
const TITLE = '2.5.4.12';
const DESCRIPTION = '2.5.4.13';
const POSTAL_ADDRESS = '2.5.4.16';
const POSTAL_CODE = '2.5.4.17';
const AUTHORITY_REVOCATION_LIST = '2.5.4.38';
const AUTHORITY_INFORMATION_ACCESS = '1.3.6.1.5.5.7.1.1';
/**
* Returns the name of the given object identifier.
*
* Some OIDs are saved as class constants in this class.
* If the wanted oidString is not among them, this method will
* query http://oid-info.com for the right name.
* This behavior can be suppressed by setting the second method parameter to false.
*
* @param string $oidString
* @param bool $loadFromWeb
*
* @see self::loadFromWeb($oidString)
*
* @return string
*/
public static function getName($oidString, $loadFromWeb = true)
{
switch ($oidString) {
case self::RSA_ENCRYPTION:
return 'RSA Encryption';
case self::MD5_WITH_RSA_ENCRYPTION:
return 'MD5 with RSA Encryption';
case self::SHA1_WITH_RSA_SIGNATURE:
return 'SHA-1 with RSA Signature';
case self::PKCS9_EMAIL:
return 'PKCS #9 Email Address';
case self::PKCS9_UNSTRUCTURED_NAME:
return 'PKCS #9 Unstructured Name';
case self::PKCS9_CONTENT_TYPE:
return 'PKCS #9 Content Type';
case self::PKCS9_MESSAGE_DIGEST:
return 'PKCS #9 Message Digest';
case self::PKCS9_SIGNING_TIME:
return 'PKCS #9 Signing Time';
case self::COMMON_NAME:
return 'Common Name';
case self::SURNAME:
return 'Surname';
case self::SERIAL_NUMBER:
return 'Serial Number';
case self::COUNTRY_NAME:
return 'Country Name';
case self::LOCALITY_NAME:
return 'Locality Name';
case self::STATE_OR_PROVINCE_NAME:
return 'State or Province Name';
case self::STREET_ADDRESS:
return 'Street Address';
case self::ORGANIZATION_NAME:
return 'Organization Name';
case self::OU_NAME:
return 'Organization Unit Name';
case self::TITLE:
return 'Title';
case self::DESCRIPTION:
return 'Description';
case self::POSTAL_ADDRESS:
return 'Postal Address';
case self::POSTAL_CODE:
return 'Postal Code';
case self::AUTHORITY_REVOCATION_LIST:
return 'Authority Revocation List';
case self::CERT_EXT_SUBJECT_DIRECTORY_ATTR:
return 'Subject directory attributes';
case self::CERT_EXT_SUBJECT_KEY_IDENTIFIER:
return 'Subject key identifier';
case self::CERT_EXT_KEY_USAGE:
return 'Key usage certificate extension';
case self::CERT_EXT_PRIVATE_KEY_USAGE_PERIOD:
return 'Private key usage';
case self::CERT_EXT_SUBJECT_ALT_NAME:
return 'Subject alternative name (SAN)';
case self::CERT_EXT_ISSUER_ALT_NAME:
return 'Issuer alternative name';
case self::CERT_EXT_BASIC_CONSTRAINTS:
return 'Basic constraints';
case self::CERT_EXT_CRL_NUMBER:
return 'CRL number';
case self::CERT_EXT_REASON_CODE:
return 'Reason code';
case self::CERT_EXT_INVALIDITY_DATE:
return 'Invalidity code';
case self::CERT_EXT_DELTA_CRL_INDICATOR:
return 'Delta CRL indicator';
case self::CERT_EXT_ISSUING_DIST_POINT:
return 'Issuing distribution point';
case self::CERT_EXT_CERT_ISSUER:
return 'Certificate issuer';
case self::CERT_EXT_NAME_CONSTRAINTS:
return 'Name constraints';
case self::CERT_EXT_CRL_DISTRIBUTION_POINTS:
return 'CRL distribution points';
case self::CERT_EXT_CERT_POLICIES:
return 'Certificate policies ';
case self::CERT_EXT_AUTHORITY_KEY_IDENTIFIER:
return 'Authority key identifier';
case self::CERT_EXT_EXTENDED_KEY_USAGE:
return 'Extended key usage';
case self::AUTHORITY_INFORMATION_ACCESS:
return 'Certificate Authority Information Access (AIA)';
default:
if ($loadFromWeb) {
return self::loadFromWeb($oidString);
} else {
return $oidString;
}
}
}
public static function loadFromWeb($oidString)
{
$ch = curl_init("http://oid-info.com/get/{$oidString}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$contents = curl_exec($ch);
curl_close($ch);
// This pattern needs to be updated as soon as the website layout of oid-info.com changes
preg_match_all('#<tt>(.+)\(\d+\)</tt>#si', $contents, $oidName);
if (empty($oidName[1])) {
return "{$oidString} (unknown)";
}
$oidName = ucfirst(strtolower(preg_replace('/([A-Z][a-z])/', ' $1', $oidName[1][0])));
$oidName = str_replace('-', ' ', $oidName);
return "{$oidName} ({$oidString})";
}
}

View file

@ -0,0 +1,342 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
use FG\ASN1\Exception\ParserException;
use FG\ASN1\Universal\BitString;
use FG\ASN1\Universal\Boolean;
use FG\ASN1\Universal\Enumerated;
use FG\ASN1\Universal\GeneralizedTime;
use FG\ASN1\Universal\Integer;
use FG\ASN1\Universal\NullObject;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\RelativeObjectIdentifier;
use FG\ASN1\Universal\OctetString;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\Set;
use FG\ASN1\Universal\UTCTime;
use FG\ASN1\Universal\IA5String;
use FG\ASN1\Universal\PrintableString;
use FG\ASN1\Universal\NumericString;
use FG\ASN1\Universal\UTF8String;
use FG\ASN1\Universal\UniversalString;
use FG\ASN1\Universal\CharacterString;
use FG\ASN1\Universal\GeneralString;
use FG\ASN1\Universal\VisibleString;
use FG\ASN1\Universal\GraphicString;
use FG\ASN1\Universal\BMPString;
use FG\ASN1\Universal\T61String;
use FG\ASN1\Universal\ObjectDescriptor;
use LogicException;
/**
* Class Object is the base class for all concrete ASN.1 objects.
*/
abstract class Object implements Parsable
{
private $contentLength;
private $nrOfLengthOctets;
/**
* Must return the number of octets of the content part.
*
* @return int
*/
abstract protected function calculateContentLength();
/**
* Encode the object using DER encoding.
*
* @see http://en.wikipedia.org/wiki/X.690#DER_encoding
*
* @return string the binary representation of an objects value
*/
abstract protected function getEncodedValue();
/**
* Return the content of this object in a non encoded form.
* This can be used to print the value in human readable form.
*
* @return mixed
*/
abstract public function getContent();
/**
* Return the object type octet.
* This should use the class constants of Identifier.
*
* @see Identifier
*
* @return int
*/
abstract public function getType();
/**
* Returns all identifier octets. If an inheriting class models a tag with
* the long form identifier format, it MUST reimplement this method to
* return all octets of the identifier.
*
* @throws LogicException If the identifier format is long form
*
* @return string Identifier as a set of octets
*/
public function getIdentifier()
{
$firstOctet = $this->getType();
if (Identifier::isLongForm($firstOctet)) {
throw new LogicException(sprintf('Identifier of %s uses the long form and must therefor override "Object::getIdentifier()".', get_class($this)));
}
return chr($firstOctet);
}
/**
* Encode this object using DER encoding.
*
* @return string the full binary representation of the complete object
*/
public function getBinary()
{
$result = $this->getIdentifier();
$result .= $this->createLengthPart();
$result .= $this->getEncodedValue();
return $result;
}
private function createLengthPart()
{
$contentLength = $this->getContentLength();
$nrOfLengthOctets = $this->getNumberOfLengthOctets($contentLength);
if ($nrOfLengthOctets == 1) {
return chr($contentLength);
} else {
// the first length octet determines the number subsequent length octets
$lengthOctets = chr(0x80 | ($nrOfLengthOctets - 1));
for ($shiftLength = 8 * ($nrOfLengthOctets - 2); $shiftLength >= 0; $shiftLength -= 8) {
$lengthOctets .= chr($contentLength >> $shiftLength);
}
return $lengthOctets;
}
}
protected function getNumberOfLengthOctets($contentLength = null)
{
if (!isset($this->nrOfLengthOctets)) {
if ($contentLength == null) {
$contentLength = $this->getContentLength();
}
$this->nrOfLengthOctets = 1;
if ($contentLength > 127) {
do { // long form
$this->nrOfLengthOctets++;
$contentLength = $contentLength >> 8;
} while ($contentLength > 0);
}
}
return $this->nrOfLengthOctets;
}
protected function getContentLength()
{
if (!isset($this->contentLength)) {
$this->contentLength = $this->calculateContentLength();
}
return $this->contentLength;
}
protected function setContentLength($newContentLength)
{
$this->contentLength = $newContentLength;
$this->getNumberOfLengthOctets($newContentLength);
}
/**
* Returns the length of the whole object (including the identifier and length octets).
*/
public function getObjectLength()
{
$nrOfIdentifierOctets = strlen($this->getIdentifier());
$contentLength = $this->getContentLength();
$nrOfLengthOctets = $this->getNumberOfLengthOctets($contentLength);
return $nrOfIdentifierOctets + $nrOfLengthOctets + $contentLength;
}
public function __toString()
{
return $this->getContent();
}
/**
* Returns the name of the ASN.1 Type of this object.
*
* @see Identifier::getName()
*/
public function getTypeName()
{
return Identifier::getName($this->getType());
}
/**
* @param string $binaryData
* @param int $offsetIndex
*
* @throws ParserException
*
* @return \FG\ASN1\Object
*/
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
if (strlen($binaryData) <= $offsetIndex) {
throw new ParserException('Can not parse binary from data: Offset index larger than input size', $offsetIndex);
}
$identifierOctet = ord($binaryData[$offsetIndex]);
if (Identifier::isContextSpecificClass($identifierOctet) && Identifier::isConstructed($identifierOctet)) {
return ExplicitlyTaggedObject::fromBinary($binaryData, $offsetIndex);
}
switch ($identifierOctet) {
case Identifier::BITSTRING:
return BitString::fromBinary($binaryData, $offsetIndex);
case Identifier::BOOLEAN:
return Boolean::fromBinary($binaryData, $offsetIndex);
case Identifier::ENUMERATED:
return Enumerated::fromBinary($binaryData, $offsetIndex);
case Identifier::INTEGER:
return Integer::fromBinary($binaryData, $offsetIndex);
case Identifier::NULL:
return NullObject::fromBinary($binaryData, $offsetIndex);
case Identifier::OBJECT_IDENTIFIER:
return ObjectIdentifier::fromBinary($binaryData, $offsetIndex);
case Identifier::RELATIVE_OID:
return RelativeObjectIdentifier::fromBinary($binaryData, $offsetIndex);
case Identifier::OCTETSTRING:
return OctetString::fromBinary($binaryData, $offsetIndex);
case Identifier::SEQUENCE:
return Sequence::fromBinary($binaryData, $offsetIndex);
case Identifier::SET:
return Set::fromBinary($binaryData, $offsetIndex);
case Identifier::UTC_TIME:
return UTCTime::fromBinary($binaryData, $offsetIndex);
case Identifier::GENERALIZED_TIME:
return GeneralizedTime::fromBinary($binaryData, $offsetIndex);
case Identifier::IA5_STRING:
return IA5String::fromBinary($binaryData, $offsetIndex);
case Identifier::PRINTABLE_STRING:
return PrintableString::fromBinary($binaryData, $offsetIndex);
case Identifier::NUMERIC_STRING:
return NumericString::fromBinary($binaryData, $offsetIndex);
case Identifier::UTF8_STRING:
return UTF8String::fromBinary($binaryData, $offsetIndex);
case Identifier::UNIVERSAL_STRING:
return UniversalString::fromBinary($binaryData, $offsetIndex);
case Identifier::CHARACTER_STRING:
return CharacterString::fromBinary($binaryData, $offsetIndex);
case Identifier::GENERAL_STRING:
return GeneralString::fromBinary($binaryData, $offsetIndex);
case Identifier::VISIBLE_STRING:
return VisibleString::fromBinary($binaryData, $offsetIndex);
case Identifier::GRAPHIC_STRING:
return GraphicString::fromBinary($binaryData, $offsetIndex);
case Identifier::BMP_STRING:
return BMPString::fromBinary($binaryData, $offsetIndex);
case Identifier::T61_STRING:
return T61String::fromBinary($binaryData, $offsetIndex);
case Identifier::OBJECT_DESCRIPTOR:
return ObjectDescriptor::fromBinary($binaryData, $offsetIndex);
default:
// At this point the identifier may be >1 byte.
if (Identifier::isConstructed($identifierOctet)) {
return new UnknownConstructedObject($binaryData, $offsetIndex);
} else {
$identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex);
$lengthOfUnknownObject = self::parseContentLength($binaryData, $offsetIndex);
$offsetIndex += $lengthOfUnknownObject;
return new UnknownObject($identifier, $lengthOfUnknownObject);
}
}
}
protected static function parseIdentifier($identifierOctet, $expectedIdentifier, $offsetForExceptionHandling)
{
if (is_string($identifierOctet) || is_numeric($identifierOctet) == false) {
$identifierOctet = ord($identifierOctet);
}
if ($identifierOctet != $expectedIdentifier) {
$message = 'Can not create an '.Identifier::getName($expectedIdentifier).' from an '.Identifier::getName($identifierOctet);
throw new ParserException($message, $offsetForExceptionHandling);
}
}
protected static function parseBinaryIdentifier($binaryData, &$offsetIndex)
{
if (strlen($binaryData) <= $offsetIndex) {
throw new ParserException('Can not parse identifier from data: Offset index larger than input size', $offsetIndex);
}
$identifier = $binaryData[$offsetIndex++];
if (Identifier::isLongForm(ord($identifier)) == false) {
return $identifier;
}
while (true) {
if (strlen($binaryData) <= $offsetIndex) {
throw new ParserException('Can not parse identifier (long form) from data: Offset index larger than input size', $offsetIndex);
}
$nextOctet = $binaryData[$offsetIndex++];
$identifier .= $nextOctet;
if ((ord($nextOctet) & 0x80) === 0) {
// the most significant bit is 0 to we have reached the end of the identifier
break;
}
}
return $identifier;
}
protected static function parseContentLength(&$binaryData, &$offsetIndex, $minimumLength = 0)
{
if (strlen($binaryData) <= $offsetIndex) {
throw new ParserException('Can not parse content length from data: Offset index larger than input size', $offsetIndex);
}
$contentLength = ord($binaryData[$offsetIndex++]);
if (($contentLength & 0x80) != 0) {
// bit 8 is set -> this is the long form
$nrOfLengthOctets = $contentLength & 0x7F;
$contentLength = 0x00;
for ($i = 0; $i < $nrOfLengthOctets; $i++) {
if (strlen($binaryData) <= $offsetIndex) {
throw new ParserException('Can not parse content length (long form) from data: Offset index larger than input size', $offsetIndex);
}
$contentLength = ($contentLength << 8) + ord($binaryData[$offsetIndex++]);
}
}
if ($contentLength < $minimumLength) {
throw new ParserException('A '.get_called_class()." should have a content length of at least {$minimumLength}. Extracted length was {$contentLength}", $offsetIndex);
}
return $contentLength;
}
}

View file

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
use FG\ASN1\Exception\ParserException;
/**
* The Parsable interface describes classes that can be parsed from their binary DER representation.
*/
interface Parsable
{
/**
* Parse an instance of this class from its binary DER encoded representation.
*
* @param string $binaryData
* @param int $offsetIndex the offset at which parsing of the $binaryData is started. This parameter ill be modified
* to contain the offset index of the next object after this object has been parsed
*
* @throws ParserException if the given binary data is either invalid or not currently supported
*
* @return static
*/
public static function fromBinary(&$binaryData, &$offsetIndex = null);
}

View file

@ -0,0 +1,70 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
use Exception;
use FG\ASN1\Exception\ParserException;
use FG\ASN1\Universal\Sequence;
class TemplateParser
{
/**
* @param string $data
* @param array $template
* @return \FG\ASN1\Object|Sequence
* @throws ParserException if there was an issue parsing
*/
public function parseBase64($data, array $template)
{
// TODO test with invalid data
return $this->parseBinary(base64_decode($data), $template);
}
/**
* @param string $binary
* @param array $template
* @return \FG\ASN1\Object|Sequence
* @throws ParserException if there was an issue parsing
*/
public function parseBinary($binary, array $template)
{
$parsedObject = Object::fromBinary($binary);
foreach ($template as $key => $value) {
$this->validate($parsedObject, $key, $value);
}
return $parsedObject;
}
private function validate(Object $object, $key, $value)
{
if (is_array($value)) {
$this->assertTypeId($key, $object);
/* @var Construct $object */
foreach ($value as $key => $child) {
$this->validate($object->current(), $key, $child);
$object->next();
}
} else {
$this->assertTypeId($value, $object);
}
}
private function assertTypeId($expectedTypeId, Object $object)
{
$actualType = $object->getType();
if ($expectedTypeId != $actualType) {
throw new Exception("Expected type ($expectedTypeId) does not match actual type ($actualType");
}
}
}

View file

@ -0,0 +1,41 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class BMPString extends AbstractString
{
/**
* Creates a new ASN.1 BMP String.
*
* BMPString is a subtype of UniversalString that has its own
* unique tag and contains only the characters in the
* Basic Multilingual Plane (those corresponding to the first
* 64K-2 cells, less cells whose encoding is used to address
* characters outside the Basic Multilingual Plane) of ISO/IEC 10646-1.
*
* TODO The encodable characters of this type are not yet checked.
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
$this->allowAll();
}
public function getType()
{
return Identifier::BMP_STRING;
}
}

View file

@ -0,0 +1,79 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use Exception;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
class BitString extends OctetString implements Parsable
{
private $nrOfUnusedBits;
/**
* Creates a new ASN.1 BitString object.
*
* @param string|int $value Either the hexadecimal value as a string (spaces are allowed - leading 0x is optional) or a numeric value
* @param int $nrOfUnusedBits the number of unused bits in the last octet [optional].
*
* @throws Exception if the second parameter is no positive numeric value
*/
public function __construct($value, $nrOfUnusedBits = 0)
{
parent::__construct($value);
if (!is_numeric($nrOfUnusedBits) || $nrOfUnusedBits < 0) {
throw new Exception('BitString: second parameter needs to be a positive number (or zero)!');
}
$this->nrOfUnusedBits = $nrOfUnusedBits;
}
public function getType()
{
return Identifier::BITSTRING;
}
protected function calculateContentLength()
{
// add one to the length for the first octet which encodes the number of unused bits in the last octet
return parent::calculateContentLength() + 1;
}
protected function getEncodedValue()
{
// the first octet determines the number of unused bits
$nrOfUnusedBitsOctet = chr($this->nrOfUnusedBits);
$actualContent = parent::getEncodedValue();
return $nrOfUnusedBitsOctet.$actualContent;
}
public function getNumberOfUnusedBits()
{
return $this->nrOfUnusedBits;
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::BITSTRING, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex, 2);
$nrOfUnusedBits = ord($binaryData[$offsetIndex]);
$value = substr($binaryData, $offsetIndex + 1, $contentLength - 1);
$offsetIndex += $contentLength;
$parsedObject = new self(bin2hex($value), $nrOfUnusedBits);
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
}

View file

@ -0,0 +1,75 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\Object;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
use FG\ASN1\Exception\ParserException;
class Boolean extends Object implements Parsable
{
private $value;
/**
* @param bool $value
*/
public function __construct($value)
{
$this->value = $value;
}
public function getType()
{
return Identifier::BOOLEAN;
}
protected function calculateContentLength()
{
return 1;
}
protected function getEncodedValue()
{
if ($this->value == false) {
return chr(0x00);
} else {
return chr(0xFF);
}
}
public function getContent()
{
if ($this->value == true) {
return 'TRUE';
} else {
return 'FALSE';
}
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::BOOLEAN, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
if ($contentLength != 1) {
throw new ParserException("An ASN.1 Boolean should not have a length other than one. Extracted length was {$contentLength}", $offsetIndex);
}
$value = ord($binaryData[$offsetIndex++]);
$booleanValue = $value == 0xFF ? true : false;
$parsedObject = new self($booleanValue);
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
}

View file

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class CharacterString extends AbstractString
{
public function __construct($string)
{
$this->value = $string;
$this->allowAll();
}
public function getType()
{
return Identifier::CHARACTER_STRING;
}
}

View file

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\Identifier;
class Enumerated extends Integer
{
public function getType()
{
return Identifier::ENUMERATED;
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class GeneralString extends AbstractString
{
/**
* Creates a new ASN.1 GeneralString.
* TODO The encodable characters of this type are not yet checked.
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
$this->allowAll();
}
public function getType()
{
return Identifier::GENERAL_STRING;
}
}

View file

@ -0,0 +1,134 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractTime;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
use FG\ASN1\Exception\ParserException;
/**
* This ASN.1 universal type contains date and time information according to ISO 8601.
*
* The type consists of values representing:
* a) a calendar date, as defined in ISO 8601; and
* b) a time of day, to any of the precisions defined in ISO 8601, except for the hours value 24 which shall not be used; and
* c) the local time differential factor as defined in ISO 8601.
*
* Decoding of this type will accept the Basic Encoding Rules (BER)
* The encoding will comply with the Distinguished Encoding Rules (DER).
*/
class GeneralizedTime extends AbstractTime implements Parsable
{
private $microseconds;
public function __construct($dateTime = null, $dateTimeZone = 'UTC')
{
parent::__construct($dateTime, $dateTimeZone);
$this->microseconds = $this->value->format('u');
if ($this->containsFractionalSecondsElement()) {
// DER requires us to remove trailing zeros
$this->microseconds = preg_replace('/([1-9]+)0+$/', '$1', $this->microseconds);
}
}
public function getType()
{
return Identifier::GENERALIZED_TIME;
}
protected function calculateContentLength()
{
$contentSize = 15; // YYYYMMDDHHmmSSZ
if ($this->containsFractionalSecondsElement()) {
$contentSize += 1 + strlen($this->microseconds);
}
return $contentSize;
}
public function containsFractionalSecondsElement()
{
return intval($this->microseconds) > 0;
}
protected function getEncodedValue()
{
$encodedContent = $this->value->format('YmdHis');
if ($this->containsFractionalSecondsElement()) {
$encodedContent .= ".{$this->microseconds}";
}
return $encodedContent.'Z';
}
public function __toString()
{
if ($this->containsFractionalSecondsElement()) {
return $this->value->format("Y-m-d\tH:i:s.uP");
} else {
return $this->value->format("Y-m-d\tH:i:sP");
}
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::GENERALIZED_TIME, $offsetIndex++);
$lengthOfMinimumTimeString = 14; // YYYYMMDDHHmmSS
$contentLength = self::parseContentLength($binaryData, $offsetIndex, $lengthOfMinimumTimeString);
$maximumBytesToRead = $contentLength;
$format = 'YmdGis';
$content = substr($binaryData, $offsetIndex, $contentLength);
$dateTimeString = substr($content, 0, $lengthOfMinimumTimeString);
$offsetIndex += $lengthOfMinimumTimeString;
$maximumBytesToRead -= $lengthOfMinimumTimeString;
if ($contentLength == $lengthOfMinimumTimeString) {
$localTimeZone = new \DateTimeZone(date_default_timezone_get());
$dateTime = \DateTime::createFromFormat($format, $dateTimeString, $localTimeZone);
} else {
if ($binaryData[$offsetIndex] == '.') {
$maximumBytesToRead--; // account for the '.'
$nrOfFractionalSecondElements = 1; // account for the '.'
while ($maximumBytesToRead > 0
&& $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '+'
&& $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '-'
&& $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != 'Z') {
$nrOfFractionalSecondElements++;
$maximumBytesToRead--;
}
$dateTimeString .= substr($binaryData, $offsetIndex, $nrOfFractionalSecondElements);
$offsetIndex += $nrOfFractionalSecondElements;
$format .= '.u';
}
$dateTime = \DateTime::createFromFormat($format, $dateTimeString, new \DateTimeZone('UTC'));
if ($maximumBytesToRead > 0) {
if ($binaryData[$offsetIndex] == '+'
|| $binaryData[$offsetIndex] == '-') {
$dateTime = static::extractTimeZoneData($binaryData, $offsetIndex, $dateTime);
} elseif ($binaryData[$offsetIndex++] != 'Z') {
throw new ParserException('Invalid ISO 8601 Time String', $offsetIndex);
}
}
}
$parsedObject = new self($dateTime);
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class GraphicString extends AbstractString
{
/**
* Creates a new ASN.1 Graphic String.
* TODO The encodable characters of this type are not yet checked.
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
$this->allowAll();
}
public function getType()
{
return Identifier::GRAPHIC_STRING;
}
}

View file

@ -0,0 +1,35 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
/**
* The International Alphabet No.5 (IA5) references the encoding of the ASCII characters.
*
* Each character in the data is encoded as 1 byte.
*/
class IA5String extends AbstractString
{
public function __construct($string)
{
parent::__construct($string);
for ($i = 1; $i < 128; $i++) {
$this->allowCharacter(chr($i));
}
}
public function getType()
{
return Identifier::IA5_STRING;
}
}

View file

@ -0,0 +1,110 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use Exception;
use FG\ASN1\Object;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
class Integer extends Object implements Parsable
{
/** @var int */
private $value;
/**
* @param int $value
*
* @throws Exception if the value is not numeric
*/
public function __construct($value)
{
if (is_numeric($value) == false) {
throw new Exception("Invalid VALUE [{$value}] for ASN1_INTEGER");
}
$this->value = $value;
}
public function getType()
{
return Identifier::INTEGER;
}
public function getContent()
{
return $this->value;
}
protected function calculateContentLength()
{
$nrOfOctets = 1; // we need at least one octet
$tmpValue = gmp_abs(gmp_init($this->value, 10));
while (gmp_cmp($tmpValue, 127) > 0) {
$tmpValue = $this->rightShift($tmpValue, 8);
$nrOfOctets++;
}
return $nrOfOctets;
}
/**
* @param resource|\GMP $number
* @param int $positions
*
* @return resource|\GMP
*/
private function rightShift($number, $positions)
{
// Shift 1 right = div / 2
return gmp_div($number, gmp_pow(2, (int) $positions));
}
protected function getEncodedValue()
{
$numericValue = gmp_init($this->value, 10);
$contentLength = $this->getContentLength();
if (gmp_sign($numericValue) < 0) {
$numericValue = gmp_add($numericValue, (gmp_sub(gmp_pow(2, 8 * $contentLength), 1)));
$numericValue = gmp_add($numericValue, 1);
}
$result = '';
for ($shiftLength = ($contentLength - 1) * 8; $shiftLength >= 0; $shiftLength -= 8) {
$octet = gmp_strval(gmp_mod($this->rightShift($numericValue, $shiftLength), 256));
$result .= chr($octet);
}
return $result;
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
$parsedObject = new static(0);
self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex, 1);
$isNegative = (ord($binaryData[$offsetIndex]) & 0x80) != 0x00;
$number = gmp_init(ord($binaryData[$offsetIndex++]) & 0x7F, 10);
for ($i = 0; $i < $contentLength - 1; $i++) {
$number = gmp_or(gmp_mul($number, 0x100), ord($binaryData[$offsetIndex++]));
}
if ($isNegative) {
$number = gmp_sub($number, gmp_pow(2, 8 * $contentLength - 1));
}
$parsedObject = new static(gmp_strval($number, 10));
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
}

View file

@ -0,0 +1,18 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
/**
* @deprecated Deprecated for future PHP 7 compatibility. Use {@link NullObject} instead.
*/
class Null extends NullObject
{
}

View file

@ -0,0 +1,54 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\Object;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
use FG\ASN1\Exception\ParserException;
class NullObject extends Object implements Parsable
{
public function getType()
{
return Identifier::NULL;
}
protected function calculateContentLength()
{
return 0;
}
protected function getEncodedValue()
{
return null;
}
public function getContent()
{
return 'NULL';
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::NULL, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
if ($contentLength != 0) {
throw new ParserException("An ASN.1 Null should not have a length other than zero. Extracted length was {$contentLength}", $offsetIndex);
}
$parsedObject = new self();
$parsedObject->setContentLength(0);
return $parsedObject;
}
}

View file

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class NumericString extends AbstractString
{
/**
* Creates a new ASN.1 NumericString.
*
* The following characters are permitted:
* Digits 0,1, ... 9
* SPACE (space)
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
$this->allowNumbers();
$this->allowSpaces();
}
public function getType()
{
return Identifier::NUMERIC_STRING;
}
}

View file

@ -0,0 +1,26 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\Identifier;
class ObjectDescriptor extends GraphicString
{
public function __construct($objectDescription)
{
parent::__construct($objectDescription);
}
public function getType()
{
return Identifier::OBJECT_DESCRIPTOR;
}
}

View file

@ -0,0 +1,138 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use Exception;
use FG\ASN1\Base128;
use FG\ASN1\OID;
use FG\ASN1\Object;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
use FG\ASN1\Exception\ParserException;
class ObjectIdentifier extends Object implements Parsable
{
protected $subIdentifiers;
protected $value;
public function __construct($value)
{
$this->subIdentifiers = explode('.', $value);
$nrOfSubIdentifiers = count($this->subIdentifiers);
for ($i = 0; $i < $nrOfSubIdentifiers; $i++) {
if (is_numeric($this->subIdentifiers[$i])) {
// enforce the integer type
$this->subIdentifiers[$i] = intval($this->subIdentifiers[$i]);
} else {
throw new Exception("[{$value}] is no valid object identifier (sub identifier ".($i + 1).' is not numeric)!');
}
}
// Merge the first to arcs of the OID registration tree (per ASN definition!)
if ($nrOfSubIdentifiers >= 2) {
$this->subIdentifiers[1] = ($this->subIdentifiers[0] * 40) + $this->subIdentifiers[1];
unset($this->subIdentifiers[0]);
}
$this->value = $value;
}
public function getContent()
{
return $this->value;
}
public function getType()
{
return Identifier::OBJECT_IDENTIFIER;
}
protected function calculateContentLength()
{
$length = 0;
foreach ($this->subIdentifiers as $subIdentifier) {
do {
$subIdentifier = $subIdentifier >> 7;
$length++;
} while ($subIdentifier > 0);
}
return $length;
}
protected function getEncodedValue()
{
$encodedValue = '';
foreach ($this->subIdentifiers as $subIdentifier) {
$encodedValue .= Base128::encode($subIdentifier);
}
return $encodedValue;
}
public function __toString()
{
return OID::getName($this->value);
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::OBJECT_IDENTIFIER, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex, 1);
$firstOctet = ord($binaryData[$offsetIndex++]);
$oidString = floor($firstOctet / 40).'.'.($firstOctet % 40);
$oidString .= '.'.self::parseOid($binaryData, $offsetIndex, $contentLength - 1);
$parsedObject = new self($oidString);
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
/**
* Parses an object identifier except for the first octet, which is parsed
* differently. This way relative object identifiers can also be parsed
* using this.
*
* @param $binaryData
* @param $offsetIndex
* @param $octetsToRead
*
* @throws ParserException
*
* @return string
*/
protected static function parseOid(&$binaryData, &$offsetIndex, $octetsToRead)
{
$oid = '';
while ($octetsToRead > 0) {
$octets = '';
do {
if (0 === $octetsToRead) {
throw new ParserException('Malformed ASN.1 Object Identifier', $offsetIndex - 1);
}
$octetsToRead--;
$octet = $binaryData[$offsetIndex++];
$octets .= $octet;
} while (ord($octet) & 0x80);
$oid .= sprintf('%d.', Base128::decode($octets));
}
// Remove trailing '.'
return substr($oid, 0, -1) ?: '';
}
}

View file

@ -0,0 +1,91 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use Exception;
use FG\ASN1\Object;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
class OctetString extends Object implements Parsable
{
protected $value;
public function __construct($value)
{
if (is_string($value)) {
// remove gaps between hex digits
$value = preg_replace('/\s|0x/', '', $value);
} elseif (is_numeric($value)) {
$value = dechex($value);
} elseif ($value === null) {
return;
} else {
throw new Exception('OctetString: unrecognized input type!');
}
if (strlen($value) % 2 != 0) {
// transform values like 1F2 to 01F2
$value = '0'.$value;
}
$this->value = $value;
}
public function getType()
{
return Identifier::OCTETSTRING;
}
protected function calculateContentLength()
{
return strlen($this->value) / 2;
}
protected function getEncodedValue()
{
$value = $this->value;
$result = '';
//Actual content
while (strlen($value) >= 2) {
// get the hex value byte by byte from the string and and add it to binary result
$result .= chr(hexdec(substr($value, 0, 2)));
$value = substr($value, 2);
}
return $result;
}
public function getContent()
{
return strtoupper($this->value);
}
public function getBinaryContent()
{
return $this->getEncodedValue();
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::OCTETSTRING, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
$value = substr($binaryData, $offsetIndex, $contentLength);
$offsetIndex += $contentLength;
$parsedObject = new self(bin2hex($value));
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
}

View file

@ -0,0 +1,53 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class PrintableString extends AbstractString
{
/**
* Creates a new ASN.1 PrintableString.
*
* The ITU-T X.680 Table 8 permits the following characters:
* Latin capital letters A,B, ... Z
* Latin small letters a,b, ... z
* Digits 0,1, ... 9
* SPACE (space)
* APOSTROPHE '
* LEFT PARENTHESIS (
* RIGHT PARENTHESIS )
* PLUS SIGN +
* COMMA ,
* HYPHEN-MINUS -
* FULL STOP .
* SOLIDUS /
* COLON :
* EQUALS SIGN =
* QUESTION MARK ?
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
$this->allowNumbers();
$this->allowAllLetters();
$this->allowSpaces();
$this->allowCharacters("'", '(', ')', '+', '-', '.', ',', '/', ':', '=', '?');
}
public function getType()
{
return Identifier::PRINTABLE_STRING;
}
}

View file

@ -0,0 +1,57 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use Exception;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
use FG\ASN1\Exception\ParserException;
class RelativeObjectIdentifier extends ObjectIdentifier implements Parsable
{
public function __construct($subIdentifiers)
{
$this->value = $subIdentifiers;
$this->subIdentifiers = explode('.', $subIdentifiers);
$nrOfSubIdentifiers = count($this->subIdentifiers);
for ($i = 0; $i < $nrOfSubIdentifiers; $i++) {
if (is_numeric($this->subIdentifiers[$i])) {
// enforce the integer type
$this->subIdentifiers[$i] = intval($this->subIdentifiers[$i]);
} else {
throw new Exception("[{$subIdentifiers}] is no valid object identifier (sub identifier ".($i + 1).' is not numeric)!');
}
}
}
public function getType()
{
return Identifier::RELATIVE_OID;
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::RELATIVE_OID, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex, 1);
try {
$oidString = self::parseOid($binaryData, $offsetIndex, $contentLength);
} catch (ParserException $e) {
throw new ParserException('Malformed ASN.1 Relative Object Identifier', $e->getOffset());
}
$parsedObject = new self($oidString);
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
}

View file

@ -0,0 +1,23 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\Construct;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
class Sequence extends Construct implements Parsable
{
public function getType()
{
return Identifier::SEQUENCE;
}
}

View file

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\Identifier;
class Set extends Sequence
{
public function getType()
{
return Identifier::SET;
}
}

View file

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class T61String extends AbstractString
{
/**
* Creates a new ASN.1 T61 String.
* TODO The encodable characters of this type are not yet checked.
*
* @see http://en.wikipedia.org/wiki/ITU_T.61
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
$this->allowAll();
}
public function getType()
{
return Identifier::T61_STRING;
}
}

View file

@ -0,0 +1,77 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractTime;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
use FG\ASN1\Exception\ParserException;
/**
* This ASN.1 universal type contains the calendar date and time.
*
* The precision is one minute or one second and optionally a
* local time differential from coordinated universal time.
*
* Decoding of this type will accept the Basic Encoding Rules (BER)
* The encoding will comply with the Distinguished Encoding Rules (DER).
*/
class UTCTime extends AbstractTime implements Parsable
{
public function getType()
{
return Identifier::UTC_TIME;
}
protected function calculateContentLength()
{
return 13; // Content is a string o the following format: YYMMDDhhmmssZ (13 octets)
}
protected function getEncodedValue()
{
return $this->value->format('ymdHis').'Z';
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::UTC_TIME, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex, 11);
$format = 'ymdGi';
$dateTimeString = substr($binaryData, $offsetIndex, 10);
$offsetIndex += 10;
// extract optional seconds part
if ($binaryData[$offsetIndex] != 'Z'
&& $binaryData[$offsetIndex] != '+'
&& $binaryData[$offsetIndex] != '-') {
$dateTimeString .= substr($binaryData, $offsetIndex, 2);
$offsetIndex += 2;
$format .= 's';
}
$dateTime = \DateTime::createFromFormat($format, $dateTimeString, new \DateTimeZone('UTC'));
// extract time zone settings
if ($binaryData[$offsetIndex] == '+'
|| $binaryData[$offsetIndex] == '-') {
$dateTime = static::extractTimeZoneData($binaryData, $offsetIndex, $dateTime);
} elseif ($binaryData[$offsetIndex++] != 'Z') {
throw new ParserException('Invalid UTC String', $offsetIndex);
}
$parsedObject = new self($dateTime);
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class UTF8String extends AbstractString
{
/**
* Creates a new ASN.1 Universal String.
* TODO The encodable characters of this type are not yet checked.
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
$this->allowAll();
}
public function getType()
{
return Identifier::UTF8_STRING;
}
}

View file

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class UniversalString extends AbstractString
{
/**
* Creates a new ASN.1 Universal String.
* TODO The encodable characters of this type are not yet checked.
*
* @see http://en.wikipedia.org/wiki/Universal_Character_Set
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
$this->allowAll();
}
public function getType()
{
return Identifier::UNIVERSAL_STRING;
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1\Universal;
use FG\ASN1\AbstractString;
use FG\ASN1\Identifier;
class VisibleString extends AbstractString
{
/**
* Creates a new ASN.1 Visible String.
* TODO The encodable characters of this type are not yet checked.
*
* @param string $string
*/
public function __construct($string)
{
$this->value = $string;
$this->allowAll();
}
public function getType()
{
return Identifier::VISIBLE_STRING;
}
}

View file

@ -0,0 +1,59 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
class UnknownConstructedObject extends Construct
{
private $identifier;
private $contentLength;
/**
* @param string $binaryData
* @param int $offsetIndex
*
* @throws \FG\ASN1\Exception\ParserException
*/
public function __construct($binaryData, &$offsetIndex)
{
$this->identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex);
$this->contentLength = self::parseContentLength($binaryData, $offsetIndex);
$children = [];
$octetsToRead = $this->contentLength;
while ($octetsToRead > 0) {
$newChild = Object::fromBinary($binaryData, $offsetIndex);
$octetsToRead -= $newChild->getObjectLength();
$children[] = $newChild;
}
parent::__construct(...$children);
}
public function getType()
{
return ord($this->identifier);
}
public function getIdentifier()
{
return $this->identifier;
}
protected function calculateContentLength()
{
return $this->contentLength;
}
protected function getEncodedValue()
{
return '';
}
}

View file

@ -0,0 +1,59 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\ASN1;
class UnknownObject extends Object
{
/** @var string */
private $value;
private $identifier;
/**
* @param string|int $identifier Either the first identifier octet as int or all identifier bytes as a string
* @param int $contentLength
*/
public function __construct($identifier, $contentLength)
{
if (is_int($identifier)) {
$identifier = chr($identifier);
}
$this->identifier = $identifier;
$this->value = "Unparsable Object ({$contentLength} bytes)";
$this->setContentLength($contentLength);
}
public function getContent()
{
return $this->value;
}
public function getType()
{
return ord($this->identifier[0]);
}
public function getIdentifier()
{
return $this->identifier;
}
protected function calculateContentLength()
{
return $this->getContentLength();
}
protected function getEncodedValue()
{
return '';
}
}

View file

@ -0,0 +1,22 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509;
use FG\ASN1\Universal\NullObject;
use FG\ASN1\Composite\AttributeTypeAndValue;
class AlgorithmIdentifier extends AttributeTypeAndValue
{
public function __construct($objectIdentifierString)
{
parent::__construct($objectIdentifierString, new NullObject());
}
}

View file

@ -0,0 +1,68 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509\CSR;
use FG\ASN1\Object;
use FG\X509\CertificateExtensions;
use FG\ASN1\OID;
use FG\ASN1\Parsable;
use FG\ASN1\Construct;
use FG\ASN1\Identifier;
use FG\ASN1\Universal\Set;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\ObjectIdentifier;
class Attributes extends Construct implements Parsable
{
public function getType()
{
return 0xA0;
}
public function addAttribute($objectIdentifier, Set $attribute)
{
if (is_string($objectIdentifier)) {
$objectIdentifier = new ObjectIdentifier($objectIdentifier);
}
$attributeSequence = new Sequence($objectIdentifier, $attribute);
$attributeSequence->getNumberOfLengthOctets(); // length and number of length octets is calculated
$this->addChild($attributeSequence);
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], 0xA0, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
$octetsToRead = $contentLength;
$parsedObject = new self();
while ($octetsToRead > 0) {
$initialOffset = $offsetIndex; // used to calculate how much bits have been read
self::parseIdentifier($binaryData[$offsetIndex], Identifier::SEQUENCE, $offsetIndex++);
self::parseContentLength($binaryData, $offsetIndex);
$objectIdentifier = ObjectIdentifier::fromBinary($binaryData, $offsetIndex);
$oidString = $objectIdentifier->getContent();
if ($oidString == OID::PKCS9_EXTENSION_REQUEST) {
$attribute = CertificateExtensions::fromBinary($binaryData, $offsetIndex);
} else {
$attribute = Object::fromBinary($binaryData, $offsetIndex);
}
$parsedObject->addAttribute($objectIdentifier, $attribute);
$octetsToRead -= ($offsetIndex - $initialOffset);
}
$parsedObject->setContentLength($contentLength);
return $parsedObject;
}
}

View file

@ -0,0 +1,137 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509\CSR;
use FG\ASN1\OID;
use FG\ASN1\Universal\Integer;
use FG\ASN1\Universal\BitString;
use FG\ASN1\Universal\Sequence;
use FG\X509\CertificateSubject;
use FG\X509\AlgorithmIdentifier;
use FG\X509\PublicKey;
class CSR extends Sequence
{
const CSR_VERSION_NR = 0;
protected $subject;
protected $publicKey;
protected $signature;
protected $signatureAlgorithm;
protected $startSequence;
/**
* @param string $commonName
* @param string $email
* @param string $organization
* @param string $locality
* @param string $state
* @param string $country
* @param string $organizationalUnit
* @param string $publicKey
* @param string $signature
* @param string $signatureAlgorithm
*/
public function __construct($commonName, $email, $organization, $locality, $state, $country, $organizationalUnit, $publicKey, $signature, $signatureAlgorithm = OID::SHA1_WITH_RSA_SIGNATURE)
{
$this->subject = new CertificateSubject(
$commonName,
$email,
$organization,
$locality,
$state,
$country,
$organizationalUnit
);
$this->publicKey = $publicKey;
$this->signature = $signature;
$this->signatureAlgorithm = $signatureAlgorithm;
$this->createCSRSequence();
}
protected function createCSRSequence()
{
$versionNr = new Integer(self::CSR_VERSION_NR);
$publicKey = new PublicKey($this->publicKey);
$signature = new BitString($this->signature);
$signatureAlgorithm = new AlgorithmIdentifier($this->signatureAlgorithm);
$certRequestInfo = new Sequence($versionNr, $this->subject, $publicKey);
$this->addChild($certRequestInfo);
$this->addChild($signatureAlgorithm);
$this->addChild($signature);
}
public function __toString()
{
$tmp = base64_encode($this->getBinary());
for ($i = 0; $i < strlen($tmp); $i++) {
if (($i + 2) % 65 == 0) {
$tmp = substr($tmp, 0, $i + 1)."\n".substr($tmp, $i + 1);
}
}
$result = '-----BEGIN CERTIFICATE REQUEST-----'.PHP_EOL;
$result .= $tmp.PHP_EOL;
$result .= '-----END CERTIFICATE REQUEST-----';
return $result;
}
public function getVersion()
{
return self::CSR_VERSION_NR;
}
public function getOrganizationName()
{
return $this->subject->getOrganization();
}
public function getLocalName()
{
return $this->subject->getLocality();
}
public function getState()
{
return $this->subject->getState();
}
public function getCountry()
{
return $this->subject->getCountry();
}
public function getOrganizationalUnit()
{
return $this->subject->getOrganizationalUnit();
}
public function getPublicKey()
{
return $this->publicKey;
}
public function getSignature()
{
return $this->signature;
}
public function getSignatureAlgorithm()
{
return $this->signatureAlgorithm;
}
}

View file

@ -0,0 +1,100 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509;
use FG\ASN1\Exception\ParserException;
use FG\ASN1\OID;
use FG\ASN1\Object;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
use FG\ASN1\Universal\OctetString;
use FG\ASN1\Universal\Set;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\X509\SAN\SubjectAlternativeNames;
class CertificateExtensions extends Set implements Parsable
{
private $innerSequence;
private $extensions = [];
public function __construct()
{
$this->innerSequence = new Sequence();
parent::__construct($this->innerSequence);
}
public function addSubjectAlternativeNames(SubjectAlternativeNames $sans)
{
$this->addExtension(OID::CERT_EXT_SUBJECT_ALT_NAME, $sans);
}
private function addExtension($oidString, Object $extension)
{
$sequence = new Sequence();
$sequence->addChild(new ObjectIdentifier($oidString));
$sequence->addChild($extension);
$this->innerSequence->addChild($sequence);
$this->extensions[] = $extension;
}
public function getContent()
{
return $this->extensions;
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::SET, $offsetIndex++);
self::parseContentLength($binaryData, $offsetIndex);
$tmpOffset = $offsetIndex;
$extensions = Sequence::fromBinary($binaryData, $offsetIndex);
$tmpOffset += 1 + $extensions->getNumberOfLengthOctets();
$parsedObject = new self();
foreach ($extensions as $extension) {
if ($extension->getType() != Identifier::SEQUENCE) {
//FIXME wrong offset index
throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Sequence but got '.$extension->getTypeName(), $offsetIndex);
}
$tmpOffset += 1 + $extension->getNumberOfLengthOctets();
$children = $extension->getChildren();
if (count($children) < 2) {
throw new ParserException('Could not parse Certificate Extensions: Needs at least two child elements per extension sequence (object identifier and octet string)', $tmpOffset);
}
/** @var \FG\ASN1\Object $objectIdentifier */
$objectIdentifier = $children[0];
/** @var OctetString $octetString */
$octetString = $children[1];
if ($objectIdentifier->getType() != Identifier::OBJECT_IDENTIFIER) {
throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Object Identifier but got '.$extension->getTypeName(), $tmpOffset);
}
$tmpOffset += $objectIdentifier->getObjectLength();
if ($objectIdentifier->getContent() == OID::CERT_EXT_SUBJECT_ALT_NAME) {
$sans = SubjectAlternativeNames::fromBinary($binaryData, $tmpOffset);
$parsedObject->addSubjectAlternativeNames($sans);
} else {
// can now only parse SANs. There might be more in the future
$tmpOffset += $octetString->getObjectLength();
}
}
$parsedObject->getBinary(); // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ )
return $parsedObject;
}
}

View file

@ -0,0 +1,108 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509;
use FG\ASN1\Composite\RelativeDistinguishedName;
use FG\ASN1\Identifier;
use FG\ASN1\OID;
use FG\ASN1\Parsable;
use FG\ASN1\Composite\RDNString;
use FG\ASN1\Universal\Sequence;
class CertificateSubject extends Sequence implements Parsable
{
private $commonName;
private $email;
private $organization;
private $locality;
private $state;
private $country;
private $organizationalUnit;
/**
* @param string $commonName
* @param string $email
* @param string $organization
* @param string $locality
* @param string $state
* @param string $country
* @param string $organizationalUnit
*/
public function __construct($commonName, $email, $organization, $locality, $state, $country, $organizationalUnit)
{
parent::__construct(
new RDNString(OID::COUNTRY_NAME, $country),
new RDNString(OID::STATE_OR_PROVINCE_NAME, $state),
new RDNString(OID::LOCALITY_NAME, $locality),
new RDNString(OID::ORGANIZATION_NAME, $organization),
new RDNString(OID::OU_NAME, $organizationalUnit),
new RDNString(OID::COMMON_NAME, $commonName),
new RDNString(OID::PKCS9_EMAIL, $email)
);
$this->commonName = $commonName;
$this->email = $email;
$this->organization = $organization;
$this->locality = $locality;
$this->state = $state;
$this->country = $country;
$this->organizationalUnit = $organizationalUnit;
}
public function getCommonName()
{
return $this->commonName;
}
public function getEmail()
{
return $this->email;
}
public function getOrganization()
{
return $this->organization;
}
public function getLocality()
{
return $this->locality;
}
public function getState()
{
return $this->state;
}
public function getCountry()
{
return $this->country;
}
public function getOrganizationalUnit()
{
return $this->organizationalUnit;
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::SEQUENCE, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
$names = [];
$octetsToRead = $contentLength;
while ($octetsToRead > 0) {
$relativeDistinguishedName = RelativeDistinguishedName::fromBinary($binaryData, $offsetIndex);
$octetsToRead -= $relativeDistinguishedName->getObjectLength();
$names[] = $relativeDistinguishedName;
}
}
}

View file

@ -0,0 +1,35 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509;
use FG\ASN1\OID;
use FG\ASN1\Universal\NullObject;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\BitString;
use FG\ASN1\Universal\ObjectIdentifier;
class PrivateKey extends Sequence
{
/**
* @param string $hexKey
* @param \FG\ASN1\Object|string $algorithmIdentifierString
*/
public function __construct($hexKey, $algorithmIdentifierString = OID::RSA_ENCRYPTION)
{
parent::__construct(
new Sequence(
new ObjectIdentifier($algorithmIdentifierString),
new NullObject()
),
new BitString($hexKey)
);
}
}

View file

@ -0,0 +1,35 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509;
use FG\ASN1\OID;
use FG\ASN1\Universal\NullObject;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\BitString;
use FG\ASN1\Universal\ObjectIdentifier;
class PublicKey extends Sequence
{
/**
* @param string $hexKey
* @param \FG\ASN1\Object|string $algorithmIdentifierString
*/
public function __construct($hexKey, $algorithmIdentifierString = OID::RSA_ENCRYPTION)
{
parent::__construct(
new Sequence(
new ObjectIdentifier($algorithmIdentifierString),
new NullObject()
),
new BitString($hexKey)
);
}
}

View file

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509\SAN;
use FG\ASN1\Universal\GeneralString;
class DNSName extends GeneralString
{
const IDENTIFIER = 0x82; // not sure yet why this is the identifier used in SAN extensions
public function __construct($dnsNameString)
{
parent::__construct($dnsNameString);
}
public function getType()
{
return self::IDENTIFIER;
}
}

View file

@ -0,0 +1,73 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509\SAN;
use FG\ASN1\Object;
use FG\ASN1\Parsable;
use FG\ASN1\Exception\ParserException;
class IPAddress extends Object implements Parsable
{
const IDENTIFIER = 0x87; // not sure yet why this is the identifier used in SAN extensions
/** @var string */
private $value;
public function __construct($ipAddressString)
{
$this->value = $ipAddressString;
}
public function getType()
{
return self::IDENTIFIER;
}
public function getContent()
{
return $this->value;
}
protected function calculateContentLength()
{
return 4;
}
protected function getEncodedValue()
{
$ipParts = explode('.', $this->value);
$binary = chr($ipParts[0]);
$binary .= chr($ipParts[1]);
$binary .= chr($ipParts[2]);
$binary .= chr($ipParts[3]);
return $binary;
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], self::IDENTIFIER, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
if ($contentLength != 4) {
throw new ParserException("A FG\\X509\SAN\IPAddress should have a content length of 4. Extracted length was {$contentLength}", $offsetIndex);
}
$ipAddressString = ord($binaryData[$offsetIndex++]).'.';
$ipAddressString .= ord($binaryData[$offsetIndex++]).'.';
$ipAddressString .= ord($binaryData[$offsetIndex++]).'.';
$ipAddressString .= ord($binaryData[$offsetIndex++]);
$parsedObject = new self($ipAddressString);
$parsedObject->getObjectLength();
return $parsedObject;
}
}

View file

@ -0,0 +1,96 @@
<?php
/*
* This file is part of the PHPASN1 library.
*
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FG\X509\SAN;
use FG\ASN1\Exception\ParserException;
use FG\ASN1\Object;
use FG\ASN1\OID;
use FG\ASN1\Parsable;
use FG\ASN1\Identifier;
use FG\ASN1\Universal\Sequence;
/**
* See section 8.3.2.1 of ITU-T X.509.
*/
class SubjectAlternativeNames extends Object implements Parsable
{
private $alternativeNamesSequence;
public function __construct()
{
$this->alternativeNamesSequence = new Sequence();
}
protected function calculateContentLength()
{
return $this->alternativeNamesSequence->getObjectLength();
}
public function getType()
{
return Identifier::OCTETSTRING;
}
public function addDomainName(DNSName $domainName)
{
$this->alternativeNamesSequence->addChild($domainName);
}
public function addIP(IPAddress $ip)
{
$this->alternativeNamesSequence->addChild($ip);
}
public function getContent()
{
return $this->alternativeNamesSequence->getContent();
}
protected function getEncodedValue()
{
return $this->alternativeNamesSequence->getBinary();
}
public static function fromBinary(&$binaryData, &$offsetIndex = 0)
{
self::parseIdentifier($binaryData[$offsetIndex], Identifier::OCTETSTRING, $offsetIndex++);
$contentLength = self::parseContentLength($binaryData, $offsetIndex);
if ($contentLength < 2) {
throw new ParserException('Can not parse Subject Alternative Names: The Sequence within the octet string after the Object identifier '.OID::CERT_EXT_SUBJECT_ALT_NAME." is too short ({$contentLength} octets)", $offsetIndex);
}
$offsetOfSequence = $offsetIndex;
$sequence = Sequence::fromBinary($binaryData, $offsetIndex);
$offsetOfSequence += $sequence->getNumberOfLengthOctets() + 1;
if ($sequence->getObjectLength() != $contentLength) {
throw new ParserException('Can not parse Subject Alternative Names: The Sequence length does not match the length of the surrounding octet string', $offsetIndex);
}
$parsedObject = new self();
/** @var \FG\ASN1\Object $object */
foreach ($sequence as $object) {
if ($object->getType() == DNSName::IDENTIFIER) {
$domainName = DNSName::fromBinary($binaryData, $offsetOfSequence);
$parsedObject->addDomainName($domainName);
} elseif ($object->getType() == IPAddress::IDENTIFIER) {
$ip = IPAddress::fromBinary($binaryData, $offsetOfSequence);
$parsedObject->addIP($ip);
} else {
throw new ParserException('Could not parse Subject Alternative Name: Only DNSName and IP SANs are currently supported', $offsetIndex);
}
}
$parsedObject->getBinary(); // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ )
return $parsedObject;
}
}

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
backupGlobals="false"
backupStaticAttributes="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="PHPASN1 Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./</directory>
<exclude>
<directory>./tests</directory>
<directory>./examples</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View file

@ -0,0 +1,89 @@
<?php
namespace FG\tests\ASN1;
use FG\ASN1\Base128;
class Base128Test extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getDataToDecode
*/
public function testEncode($value, $expected)
{
$this->assertEquals($expected, Base128::encode($value));
}
/**
* @dataProvider getDataToDecode
*/
public function testDecode($expected, $octets)
{
$this->assertEquals($expected, Base128::decode($octets));
}
public function getDataToDecode()
{
return [
[0x00000000, "\x00"],
// self explanatory
[0x0000003F, "\x3F"],
// binary: 00111111
// septets: 0111111
// padded: 00111111
// hex: 0x3F
[0x0000007F, "\x7F"],
// binary: 01111111
// septets: 1111111
// padded: 01111111
// hex: 0x7F
[0x00000080, "\x81\x00"],
// binary: 10000000
// septets: 0000001 00000000
// padded: 10000001 00000000
// hex: 0x81 0x00
[0x00004001, "\x81\x80\x01"],
// binary: 01000000 00000001
// septets: 01 0000000 0000001
// padded: 10000001 10000000 00000001
// hex: 0x81 0x80 0x01
[0x00002000, "\xC0\x00"],
// binary: 00100000 00000000
// septets: 1000000 0000000
// padded: 11000000 00000000
// hex: 0xC0 0x00
[0x001FFFFF, "\xFF\xFF\x7F"],
// binary: 00011111 11111111 11111111
// septet: 1111111 1111111 1111111
// padded: 11111111 11111111 01111111
// hex: 0xFF 0xFF 0x7F
[0x0FFFFFFF, "\xFF\xFF\xFF\x7F"],
// binary: 00001111 11111111 11111111 11111111
// septet: 1111111 1111111 1111111 1111111
// padded: 11111111 11111111 11111111 01111111
// hex: 0xFF 0xFF 0xFF 0x7F
[0xA34B253, "\xD1\xD2\xE4\x53"],
// binary: 00001010 00110100 10110010 01010011
// septet: 1010001 1010010 1100100 1010011
// padded: 11010001 11010010 11100100 01010011
// hex: 0xD1 0xD2 0xE4 0x53
];
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Malformed base-128 encoded value (0xFFFF).
*/
public function testDecodeFailsIfLastOctetSignificantBitSet()
{
Base128::decode("\xFF\xFF");
}
}

Some files were not shown because too many files have changed in this diff Show more