Update website
This commit is contained in:
parent
bb4b0f9be8
commit
011b183e28
4263 changed files with 3014 additions and 720369 deletions
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Spomky-Labs
|
||||
|
||||
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.
|
|
@ -1,39 +0,0 @@
|
|||
{
|
||||
"name": "web-auth/metadata-service",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"description": "Metadata Service for FIDO2/Webauthn",
|
||||
"keywords": ["FIDO", "FIDO2", "webauthn"],
|
||||
"homepage": "https://github.com/web-auth",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Florent Morselli",
|
||||
"homepage": "https://github.com/Spomky"
|
||||
},
|
||||
{
|
||||
"name": "All contributors",
|
||||
"homepage": "https://github.com/web-auth/metadata-service/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"ext-json": "*",
|
||||
"beberlei/assert": "^3.2",
|
||||
"league/uri": "^6.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/log": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log-implementation": "Recommended to receive logs from the library"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Webauthn\\MetadataService\\": "src/"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"web-token/jwt-key-mgmt": "Mandatory for fetching Metadata Statement from distant sources",
|
||||
"web-token/jwt-signature-algorithm-ecdsa": "Mandatory for fetching Metadata Statement from distant sources"
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use JsonSerializable;
|
||||
|
||||
abstract class AbstractDescriptor implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $maxRetries;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $blockSlowdown;
|
||||
|
||||
public function __construct(?int $maxRetries = null, ?int $blockSlowdown = null)
|
||||
{
|
||||
Assertion::greaterOrEqualThan($maxRetries, 0, Utils::logicException('Invalid data. The value of "maxRetries" must be a positive integer'));
|
||||
Assertion::greaterOrEqualThan($blockSlowdown, 0, Utils::logicException('Invalid data. The value of "blockSlowdown" must be a positive integer'));
|
||||
|
||||
$this->maxRetries = $maxRetries;
|
||||
$this->blockSlowdown = $blockSlowdown;
|
||||
}
|
||||
|
||||
public function getMaxRetries(): ?int
|
||||
{
|
||||
return $this->maxRetries;
|
||||
}
|
||||
|
||||
public function getBlockSlowdown(): ?int
|
||||
{
|
||||
return $this->blockSlowdown;
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
abstract class AuthenticatorStatus
|
||||
{
|
||||
public const NOT_FIDO_CERTIFIED = 'NOT_FIDO_CERTIFIED';
|
||||
public const FIDO_CERTIFIED = 'FIDO_CERTIFIED';
|
||||
public const USER_VERIFICATION_BYPASS = 'USER_VERIFICATION_BYPASS';
|
||||
public const ATTESTATION_KEY_COMPROMISE = 'ATTESTATION_KEY_COMPROMISE';
|
||||
public const USER_KEY_REMOTE_COMPROMISE = 'USER_KEY_REMOTE_COMPROMISE';
|
||||
public const USER_KEY_PHYSICAL_COMPROMISE = 'USER_KEY_PHYSICAL_COMPROMISE';
|
||||
public const UPDATE_AVAILABLE = 'UPDATE_AVAILABLE';
|
||||
public const REVOKED = 'REVOKED';
|
||||
public const SELF_ASSERTION_SUBMITTED = 'SELF_ASSERTION_SUBMITTED';
|
||||
public const FIDO_CERTIFIED_L1 = 'FIDO_CERTIFIED_L1';
|
||||
public const FIDO_CERTIFIED_L1plus = 'FIDO_CERTIFIED_L1plus';
|
||||
public const FIDO_CERTIFIED_L2 = 'FIDO_CERTIFIED_L2';
|
||||
public const FIDO_CERTIFIED_L2plus = 'FIDO_CERTIFIED_L2plus';
|
||||
public const FIDO_CERTIFIED_L3 = 'FIDO_CERTIFIED_L3';
|
||||
public const FIDO_CERTIFIED_L3plus = 'FIDO_CERTIFIED_L3plus';
|
||||
public const FIDO_CERTIFIED_L4 = 'FIDO_CERTIFIED_L4';
|
||||
public const FIDO_CERTIFIED_L5 = 'FIDO_CERTIFIED_L5';
|
||||
|
||||
public static function list(): array
|
||||
{
|
||||
return [
|
||||
self::NOT_FIDO_CERTIFIED,
|
||||
self::FIDO_CERTIFIED,
|
||||
self::USER_VERIFICATION_BYPASS,
|
||||
self::ATTESTATION_KEY_COMPROMISE,
|
||||
self::USER_KEY_REMOTE_COMPROMISE,
|
||||
self::USER_KEY_PHYSICAL_COMPROMISE,
|
||||
self::UPDATE_AVAILABLE,
|
||||
self::REVOKED,
|
||||
self::SELF_ASSERTION_SUBMITTED,
|
||||
self::FIDO_CERTIFIED_L1,
|
||||
self::FIDO_CERTIFIED_L1plus,
|
||||
self::FIDO_CERTIFIED_L2,
|
||||
self::FIDO_CERTIFIED_L2plus,
|
||||
self::FIDO_CERTIFIED_L3,
|
||||
self::FIDO_CERTIFIED_L3plus,
|
||||
self::FIDO_CERTIFIED_L4,
|
||||
self::FIDO_CERTIFIED_L5,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
|
||||
class BiometricAccuracyDescriptor extends AbstractDescriptor
|
||||
{
|
||||
/**
|
||||
* @var float|null
|
||||
*/
|
||||
private $FAR;
|
||||
|
||||
/**
|
||||
* @var float|null
|
||||
*/
|
||||
private $FRR;
|
||||
|
||||
/**
|
||||
* @var float|null
|
||||
*/
|
||||
private $EER;
|
||||
|
||||
/**
|
||||
* @var float|null
|
||||
*/
|
||||
private $FAAR;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $maxReferenceDataSets;
|
||||
|
||||
public function __construct(?float $FAR, ?float $FRR, ?float $EER, ?float $FAAR, ?int $maxReferenceDataSets, ?int $maxRetries = null, ?int $blockSlowdown = null)
|
||||
{
|
||||
Assertion::greaterOrEqualThan($maxReferenceDataSets, 0, Utils::logicException('Invalid data. The value of "maxReferenceDataSets" must be a positive integer'));
|
||||
$this->FRR = $FRR;
|
||||
$this->FAR = $FAR;
|
||||
$this->EER = $EER;
|
||||
$this->FAAR = $FAAR;
|
||||
$this->maxReferenceDataSets = $maxReferenceDataSets;
|
||||
parent::__construct($maxRetries, $blockSlowdown);
|
||||
}
|
||||
|
||||
public function getFAR(): ?float
|
||||
{
|
||||
return $this->FAR;
|
||||
}
|
||||
|
||||
public function getFRR(): ?float
|
||||
{
|
||||
return $this->FRR;
|
||||
}
|
||||
|
||||
public function getEER(): ?float
|
||||
{
|
||||
return $this->EER;
|
||||
}
|
||||
|
||||
public function getFAAR(): ?float
|
||||
{
|
||||
return $this->FAAR;
|
||||
}
|
||||
|
||||
public function getMaxReferenceDataSets(): ?int
|
||||
{
|
||||
return $this->maxReferenceDataSets;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
return new self(
|
||||
$data['FAR'] ?? null,
|
||||
$data['FRR'] ?? null,
|
||||
$data['EER'] ?? null,
|
||||
$data['FAAR'] ?? null,
|
||||
$data['maxReferenceDataSets'] ?? null,
|
||||
$data['maxRetries'] ?? null,
|
||||
$data['blockSlowdown'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'FAR' => $this->FAR,
|
||||
'FRR' => $this->FRR,
|
||||
'EER' => $this->EER,
|
||||
'FAAR' => $this->FAAR,
|
||||
'maxReferenceDataSets' => $this->maxReferenceDataSets,
|
||||
'maxRetries' => $this->getMaxRetries(),
|
||||
'blockSlowdown' => $this->getBlockSlowdown(),
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
class BiometricStatusReport implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $certLevel;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $modality;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $effectiveDate;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $certificationDescriptor;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $certificateNumber;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $certificationPolicyVersion;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $certificationRequirementsVersion;
|
||||
|
||||
public function getCertLevel(): int
|
||||
{
|
||||
return $this->certLevel;
|
||||
}
|
||||
|
||||
public function getModality(): int
|
||||
{
|
||||
return $this->modality;
|
||||
}
|
||||
|
||||
public function getEffectiveDate(): ?string
|
||||
{
|
||||
return $this->effectiveDate;
|
||||
}
|
||||
|
||||
public function getCertificationDescriptor(): ?string
|
||||
{
|
||||
return $this->certificationDescriptor;
|
||||
}
|
||||
|
||||
public function getCertificateNumber(): ?string
|
||||
{
|
||||
return $this->certificateNumber;
|
||||
}
|
||||
|
||||
public function getCertificationPolicyVersion(): ?string
|
||||
{
|
||||
return $this->certificationPolicyVersion;
|
||||
}
|
||||
|
||||
public function getCertificationRequirementsVersion(): ?string
|
||||
{
|
||||
return $this->certificationRequirementsVersion;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$object = new self();
|
||||
$object->certLevel = $data['certLevel'] ?? null;
|
||||
$object->modality = $data['modality'] ?? null;
|
||||
$object->effectiveDate = $data['effectiveDate'] ?? null;
|
||||
$object->certificationDescriptor = $data['certificationDescriptor'] ?? null;
|
||||
$object->certificateNumber = $data['certificateNumber'] ?? null;
|
||||
$object->certificationPolicyVersion = $data['certificationPolicyVersion'] ?? null;
|
||||
$object->certificationRequirementsVersion = $data['certificationRequirementsVersion'] ?? null;
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'certLevel' => $this->certLevel,
|
||||
'modality' => $this->modality,
|
||||
'effectiveDate' => $this->effectiveDate,
|
||||
'certificationDescriptor' => $this->certificationDescriptor,
|
||||
'certificateNumber' => $this->certificateNumber,
|
||||
'certificationPolicyVersion' => $this->certificationPolicyVersion,
|
||||
'certificationRequirementsVersion' => $this->certificationRequirementsVersion,
|
||||
];
|
||||
|
||||
return array_filter($data, static function ($var): bool {return null !== $var; });
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
|
||||
class CodeAccuracyDescriptor extends AbstractDescriptor
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $base;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $minLength;
|
||||
|
||||
public function __construct(int $base, int $minLength, ?int $maxRetries = null, ?int $blockSlowdown = null)
|
||||
{
|
||||
Assertion::greaterOrEqualThan($base, 0, Utils::logicException('Invalid data. The value of "base" must be a positive integer'));
|
||||
Assertion::greaterOrEqualThan($minLength, 0, Utils::logicException('Invalid data. The value of "minLength" must be a positive integer'));
|
||||
$this->base = $base;
|
||||
$this->minLength = $minLength;
|
||||
parent::__construct($maxRetries, $blockSlowdown);
|
||||
}
|
||||
|
||||
public function getBase(): int
|
||||
{
|
||||
return $this->base;
|
||||
}
|
||||
|
||||
public function getMinLength(): int
|
||||
{
|
||||
return $this->minLength;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
Assertion::keyExists($data, 'base', Utils::logicException('The parameter "base" is missing'));
|
||||
Assertion::keyExists($data, 'minLength', Utils::logicException('The parameter "minLength" is missing'));
|
||||
|
||||
return new self(
|
||||
$data['base'],
|
||||
$data['minLength'],
|
||||
$data['maxRetries'] ?? null,
|
||||
$data['blockSlowdown'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'base' => $this->base,
|
||||
'minLength' => $this->minLength,
|
||||
'maxRetries' => $this->getMaxRetries(),
|
||||
'blockSlowdown' => $this->getBlockSlowdown(),
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use JsonSerializable;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class DisplayPNGCharacteristicsDescriptor implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $width;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $height;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $bitDepth;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $colorType;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $compression;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $filter;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $interlace;
|
||||
|
||||
/**
|
||||
* @var RgbPaletteEntry[]
|
||||
*/
|
||||
private $plte = [];
|
||||
|
||||
public function __construct(int $width, int $height, int $bitDepth, int $colorType, int $compression, int $filter, int $interlace)
|
||||
{
|
||||
Assertion::greaterOrEqualThan($width, 0, Utils::logicException('Invalid width'));
|
||||
Assertion::greaterOrEqualThan($height, 0, Utils::logicException('Invalid height'));
|
||||
Assertion::range($bitDepth, 0, 254, Utils::logicException('Invalid bit depth'));
|
||||
Assertion::range($colorType, 0, 254, Utils::logicException('Invalid color type'));
|
||||
Assertion::range($compression, 0, 254, Utils::logicException('Invalid compression'));
|
||||
Assertion::range($filter, 0, 254, Utils::logicException('Invalid filter'));
|
||||
Assertion::range($interlace, 0, 254, Utils::logicException('Invalid interlace'));
|
||||
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
$this->bitDepth = $bitDepth;
|
||||
$this->colorType = $colorType;
|
||||
$this->compression = $compression;
|
||||
$this->filter = $filter;
|
||||
$this->interlace = $interlace;
|
||||
}
|
||||
|
||||
public function addPalette(RgbPaletteEntry $rgbPaletteEntry): self
|
||||
{
|
||||
$this->plte[] = $rgbPaletteEntry;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWidth(): int
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
public function getHeight(): int
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
public function getBitDepth(): int
|
||||
{
|
||||
return $this->bitDepth;
|
||||
}
|
||||
|
||||
public function getColorType(): int
|
||||
{
|
||||
return $this->colorType;
|
||||
}
|
||||
|
||||
public function getCompression(): int
|
||||
{
|
||||
return $this->compression;
|
||||
}
|
||||
|
||||
public function getFilter(): int
|
||||
{
|
||||
return $this->filter;
|
||||
}
|
||||
|
||||
public function getInterlace(): int
|
||||
{
|
||||
return $this->interlace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RgbPaletteEntry[]
|
||||
*/
|
||||
public function getPlte(): array
|
||||
{
|
||||
return $this->plte;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$data = Utils::filterNullValues($data);
|
||||
foreach (['width', 'compression', 'height', 'bitDepth', 'colorType', 'compression', 'filter', 'interlace'] as $key) {
|
||||
Assertion::keyExists($data, $key, sprintf('Invalid data. The key "%s" is missing', $key));
|
||||
}
|
||||
$object = new self(
|
||||
$data['width'],
|
||||
$data['height'],
|
||||
$data['bitDepth'],
|
||||
$data['colorType'],
|
||||
$data['compression'],
|
||||
$data['filter'],
|
||||
$data['interlace']
|
||||
);
|
||||
if (isset($data['plte'])) {
|
||||
$plte = $data['plte'];
|
||||
Assertion::isArray($plte, Utils::logicException('Invalid "plte" parameter'));
|
||||
foreach ($plte as $item) {
|
||||
$object->addPalette(RgbPaletteEntry::createFromArray($item));
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'width' => $this->width,
|
||||
'height' => $this->height,
|
||||
'bitDepth' => $this->bitDepth,
|
||||
'colorType' => $this->colorType,
|
||||
'compression' => $this->compression,
|
||||
'filter' => $this->filter,
|
||||
'interlace' => $this->interlace,
|
||||
'plte' => $this->plte,
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use Base64Url\Base64Url;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use function Safe\json_decode;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class DistantSingleMetadata extends SingleMetadata
|
||||
{
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $httpClient;
|
||||
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
private $requestFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $additionalHeaders;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $uri;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isBase64Encoded;
|
||||
|
||||
public function __construct(string $uri, bool $isBase64Encoded, ClientInterface $httpClient, RequestFactoryInterface $requestFactory, array $additionalHeaders = [])
|
||||
{
|
||||
parent::__construct($uri, $isBase64Encoded); //Useless
|
||||
$this->uri = $uri;
|
||||
$this->isBase64Encoded = $isBase64Encoded;
|
||||
$this->httpClient = $httpClient;
|
||||
$this->requestFactory = $requestFactory;
|
||||
$this->additionalHeaders = $additionalHeaders;
|
||||
}
|
||||
|
||||
public function getMetadataStatement(): MetadataStatement
|
||||
{
|
||||
$payload = $this->fetch();
|
||||
$json = $this->isBase64Encoded ? Base64Url::decode($payload) : $payload;
|
||||
$data = json_decode($json, true);
|
||||
|
||||
return MetadataStatement::createFromArray($data);
|
||||
}
|
||||
|
||||
private function fetch(): string
|
||||
{
|
||||
$request = $this->requestFactory->createRequest('GET', $this->uri);
|
||||
foreach ($this->additionalHeaders as $k => $v) {
|
||||
$request = $request->withHeader($k, $v);
|
||||
}
|
||||
$response = $this->httpClient->sendRequest($request);
|
||||
Assertion::eq(200, $response->getStatusCode(), sprintf('Unable to contact the server. Response code is %d', $response->getStatusCode()));
|
||||
$content = $response->getBody()->getContents();
|
||||
Assertion::notEmpty($content, 'Unable to contact the server. The response has no content');
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use Base64Url\Base64Url;
|
||||
use JsonSerializable;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class EcdaaTrustAnchor implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $X;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $Y;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $c;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sx;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sy;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $G1Curve;
|
||||
|
||||
public function __construct(string $X, string $Y, string $c, string $sx, string $sy, string $G1Curve)
|
||||
{
|
||||
$this->X = $X;
|
||||
$this->Y = $Y;
|
||||
$this->c = $c;
|
||||
$this->sx = $sx;
|
||||
$this->sy = $sy;
|
||||
$this->G1Curve = $G1Curve;
|
||||
}
|
||||
|
||||
public function getX(): string
|
||||
{
|
||||
return $this->X;
|
||||
}
|
||||
|
||||
public function getY(): string
|
||||
{
|
||||
return $this->Y;
|
||||
}
|
||||
|
||||
public function getC(): string
|
||||
{
|
||||
return $this->c;
|
||||
}
|
||||
|
||||
public function getSx(): string
|
||||
{
|
||||
return $this->sx;
|
||||
}
|
||||
|
||||
public function getSy(): string
|
||||
{
|
||||
return $this->sy;
|
||||
}
|
||||
|
||||
public function getG1Curve(): string
|
||||
{
|
||||
return $this->G1Curve;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$data = Utils::filterNullValues($data);
|
||||
foreach (['X', 'Y', 'c', 'sx', 'sy', 'G1Curve'] as $key) {
|
||||
Assertion::keyExists($data, $key, sprintf('Invalid data. The key "%s" is missing', $key));
|
||||
}
|
||||
|
||||
return new self(
|
||||
Base64Url::decode($data['X']),
|
||||
Base64Url::decode($data['Y']),
|
||||
Base64Url::decode($data['c']),
|
||||
Base64Url::decode($data['sx']),
|
||||
Base64Url::decode($data['sy']),
|
||||
$data['G1Curve']
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'X' => Base64Url::encode($this->X),
|
||||
'Y' => Base64Url::encode($this->Y),
|
||||
'c' => Base64Url::encode($this->c),
|
||||
'sx' => Base64Url::encode($this->sx),
|
||||
'sy' => Base64Url::encode($this->sy),
|
||||
'G1Curve' => $this->G1Curve,
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use function array_key_exists;
|
||||
use Assert\Assertion;
|
||||
use JsonSerializable;
|
||||
|
||||
class ExtensionDescriptor implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $tag;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $fail_if_unknown;
|
||||
|
||||
public function __construct(string $id, ?int $tag, ?string $data, bool $fail_if_unknown)
|
||||
{
|
||||
if (null !== $tag) {
|
||||
Assertion::greaterOrEqualThan($tag, 0, Utils::logicException('Invalid data. The parameter "tag" shall be a positive integer'));
|
||||
}
|
||||
$this->id = $id;
|
||||
$this->tag = $tag;
|
||||
$this->data = $data;
|
||||
$this->fail_if_unknown = $fail_if_unknown;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getTag(): ?int
|
||||
{
|
||||
return $this->tag;
|
||||
}
|
||||
|
||||
public function getData(): ?string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function isFailIfUnknown(): bool
|
||||
{
|
||||
return $this->fail_if_unknown;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$data = Utils::filterNullValues($data);
|
||||
Assertion::keyExists($data, 'id', Utils::logicException('Invalid data. The parameter "id" is missing'));
|
||||
Assertion::string($data['id'], Utils::logicException('Invalid data. The parameter "id" shall be a string'));
|
||||
Assertion::keyExists($data, 'fail_if_unknown', Utils::logicException('Invalid data. The parameter "fail_if_unknown" is missing'));
|
||||
Assertion::boolean($data['fail_if_unknown'], Utils::logicException('Invalid data. The parameter "fail_if_unknown" shall be a boolean'));
|
||||
if (array_key_exists('tag', $data)) {
|
||||
Assertion::integer($data['tag'], Utils::logicException('Invalid data. The parameter "tag" shall be a positive integer'));
|
||||
}
|
||||
if (array_key_exists('data', $data)) {
|
||||
Assertion::string($data['data'], Utils::logicException('Invalid data. The parameter "data" shall be a string'));
|
||||
}
|
||||
|
||||
return new self(
|
||||
$data['id'],
|
||||
$data['tag'] ?? null,
|
||||
$data['data'] ?? null,
|
||||
$data['fail_if_unknown']
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$result = [
|
||||
'id' => $this->id,
|
||||
'tag' => $this->tag,
|
||||
'data' => $this->data,
|
||||
'fail_if_unknown' => $this->fail_if_unknown,
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($result);
|
||||
}
|
||||
}
|
|
@ -1,283 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use Base64Url\Base64Url;
|
||||
use function count;
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Jose\Component\Signature\Algorithm\ES256;
|
||||
use Jose\Component\Signature\Serializer\CompactSerializer;
|
||||
use League\Uri\UriString;
|
||||
use LogicException;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use function Safe\json_decode;
|
||||
use function Safe\sprintf;
|
||||
use Throwable;
|
||||
use Webauthn\CertificateToolbox;
|
||||
|
||||
class MetadataService
|
||||
{
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $httpClient;
|
||||
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
private $requestFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $additionalQueryStringValues;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $additionalHeaders;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $serviceUri;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
public function __construct(string $serviceUri, ClientInterface $httpClient, RequestFactoryInterface $requestFactory, array $additionalQueryStringValues = [], array $additionalHeaders = [], ?LoggerInterface $logger = null)
|
||||
{
|
||||
if (0 !== count($additionalQueryStringValues)) {
|
||||
@trigger_error('The argument "additionalQueryStringValues" is deprecated since version 3.3 and will be removed in 4.0. Please set an empty array instead and us the method `addQueryStringValues`.', E_USER_DEPRECATED);
|
||||
}
|
||||
if (0 !== count($additionalQueryStringValues)) {
|
||||
@trigger_error('The argument "additionalHeaders" is deprecated since version 3.3 and will be removed in 4.0. Please set an empty array instead and us the method `addHeaders`.', E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $logger) {
|
||||
@trigger_error('The argument "logger" is deprecated since version 3.3 and will be removed in 4.0. Please use the method "setLogger" instead.', E_USER_DEPRECATED);
|
||||
}
|
||||
$this->serviceUri = $serviceUri;
|
||||
$this->httpClient = $httpClient;
|
||||
$this->requestFactory = $requestFactory;
|
||||
$this->additionalQueryStringValues = $additionalQueryStringValues;
|
||||
$this->additionalHeaders = $additionalHeaders;
|
||||
$this->logger = $logger ?? new NullLogger();
|
||||
}
|
||||
|
||||
public function addQueryStringValues(array $additionalQueryStringValues): self
|
||||
{
|
||||
$this->additionalQueryStringValues = $additionalQueryStringValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addHeaders(array $additionalHeaders): self
|
||||
{
|
||||
$this->additionalHeaders = $additionalHeaders;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLogger(LoggerInterface $logger): self
|
||||
{
|
||||
$this->logger = $logger;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function has(string $aaguid): bool
|
||||
{
|
||||
try {
|
||||
$toc = $this->fetchMetadataTOCPayload();
|
||||
} catch (Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
foreach ($toc->getEntries() as $entry) {
|
||||
if ($entry->getAaguid() === $aaguid && null !== $entry->getUrl()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get(string $aaguid): MetadataStatement
|
||||
{
|
||||
$toc = $this->fetchMetadataTOCPayload();
|
||||
foreach ($toc->getEntries() as $entry) {
|
||||
if ($entry->getAaguid() === $aaguid && null !== $entry->getUrl()) {
|
||||
$mds = $this->fetchMetadataStatementFor($entry);
|
||||
$mds
|
||||
->setStatusReports($entry->getStatusReports())
|
||||
->setRootCertificates($toc->getRootCertificates())
|
||||
;
|
||||
|
||||
return $mds;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('The Metadata Statement with AAGUID "%s" is missing', $aaguid));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is deprecated since v3.3 and will be removed in v4.0
|
||||
*/
|
||||
public function getMetadataStatementFor(MetadataTOCPayloadEntry $entry, string $hashingFunction = 'sha256'): MetadataStatement
|
||||
{
|
||||
return $this->fetchMetadataStatementFor($entry, $hashingFunction);
|
||||
}
|
||||
|
||||
public function fetchMetadataStatementFor(MetadataTOCPayloadEntry $entry, string $hashingFunction = 'sha256'): MetadataStatement
|
||||
{
|
||||
$this->logger->info('Trying to get the metadata statement for a given entry', ['entry' => $entry]);
|
||||
try {
|
||||
$hash = $entry->getHash();
|
||||
$url = $entry->getUrl();
|
||||
if (null === $hash || null === $url) {
|
||||
throw new LogicException('The Metadata Statement has not been published');
|
||||
}
|
||||
$uri = $this->buildUri($url);
|
||||
$result = $this->fetchMetadataStatement($uri, true, $hash, $hashingFunction);
|
||||
$this->logger->info('The metadata statement exists');
|
||||
$this->logger->debug('Metadata Statement', ['mds' => $result]);
|
||||
|
||||
return $result;
|
||||
} catch (Throwable $throwable) {
|
||||
$this->logger->error('An error occurred', [
|
||||
'exception' => $throwable,
|
||||
]);
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is deprecated since v3.3 and will be removed in v4.0
|
||||
*/
|
||||
public function getMetadataTOCPayload(): MetadataTOCPayload
|
||||
{
|
||||
return $this->fetchMetadataTOCPayload();
|
||||
}
|
||||
|
||||
private function fetchMetadataTOCPayload(): MetadataTOCPayload
|
||||
{
|
||||
$this->logger->info('Trying to get the metadata service TOC payload');
|
||||
try {
|
||||
$uri = $this->buildUri($this->serviceUri);
|
||||
$toc = $this->fetchTableOfContent($uri);
|
||||
$this->logger->info('The TOC payload has been received');
|
||||
$this->logger->debug('TOC payload', ['toc' => $toc]);
|
||||
|
||||
return $toc;
|
||||
} catch (Throwable $throwable) {
|
||||
$this->logger->error('An error occurred', [
|
||||
'exception' => $throwable,
|
||||
]);
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildUri(string $uri): string
|
||||
{
|
||||
$parsedUri = UriString::parse($uri);
|
||||
$queryString = $parsedUri['query'];
|
||||
$query = [];
|
||||
if (null !== $queryString) {
|
||||
parse_str($queryString, $query);
|
||||
}
|
||||
foreach ($this->additionalQueryStringValues as $k => $v) {
|
||||
if (!isset($query[$k])) {
|
||||
$query[$k] = $v;
|
||||
continue;
|
||||
}
|
||||
if (!is_array($query[$k])) {
|
||||
$query[$k] = [$query[$k], $v];
|
||||
continue;
|
||||
}
|
||||
$query[$k][] = $v;
|
||||
}
|
||||
$parsedUri['query'] = 0 === count($query) ? null : http_build_query($query, '', '&', PHP_QUERY_RFC3986);
|
||||
|
||||
return UriString::build($parsedUri);
|
||||
}
|
||||
|
||||
private function fetchTableOfContent(string $uri): MetadataTOCPayload
|
||||
{
|
||||
$content = $this->fetch($uri);
|
||||
$rootCertificates = [];
|
||||
$payload = $this->getJwsPayload($content, $rootCertificates);
|
||||
$data = json_decode($payload, true);
|
||||
|
||||
$toc = MetadataTOCPayload::createFromArray($data);
|
||||
$toc->setRootCertificates($rootCertificates);
|
||||
|
||||
return $toc;
|
||||
}
|
||||
|
||||
private function fetchMetadataStatement(string $uri, bool $isBase64UrlEncoded, string $hash = '', string $hashingFunction = 'sha256'): MetadataStatement
|
||||
{
|
||||
$payload = $this->fetch($uri);
|
||||
if ('' !== $hash) {
|
||||
Assertion::true(hash_equals($hash, hash($hashingFunction, $payload, true)), 'The hash cannot be verified. The metadata statement shall be rejected');
|
||||
}
|
||||
$json = $isBase64UrlEncoded ? Base64Url::decode($payload) : $payload;
|
||||
$data = json_decode($json, true);
|
||||
|
||||
return MetadataStatement::createFromArray($data);
|
||||
}
|
||||
|
||||
private function fetch(string $uri): string
|
||||
{
|
||||
$request = $this->requestFactory->createRequest('GET', $uri);
|
||||
foreach ($this->additionalHeaders as $k => $v) {
|
||||
$request = $request->withHeader($k, $v);
|
||||
}
|
||||
$response = $this->httpClient->sendRequest($request);
|
||||
Assertion::eq(200, $response->getStatusCode(), sprintf('Unable to contact the server. Response code is %d', $response->getStatusCode()));
|
||||
$content = $response->getBody()->getContents();
|
||||
Assertion::notEmpty($content, 'Unable to contact the server. The response has no content');
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function getJwsPayload(string $token, array &$rootCertificates): string
|
||||
{
|
||||
$jws = (new CompactSerializer())->unserialize($token);
|
||||
Assertion::eq(1, $jws->countSignatures(), 'Invalid response from the metadata service. Only one signature shall be present.');
|
||||
$signature = $jws->getSignature(0);
|
||||
$payload = $jws->getPayload();
|
||||
Assertion::notEmpty($payload, 'Invalid response from the metadata service. The token payload is empty.');
|
||||
$header = $signature->getProtectedHeader();
|
||||
Assertion::keyExists($header, 'alg', 'The "alg" parameter is missing.');
|
||||
Assertion::eq($header['alg'], 'ES256', 'The expected "alg" parameter value should be "ES256".');
|
||||
Assertion::keyExists($header, 'x5c', 'The "x5c" parameter is missing.');
|
||||
Assertion::isArray($header['x5c'], 'The "x5c" parameter should be an array.');
|
||||
$key = JWKFactory::createFromX5C($header['x5c']);
|
||||
$rootCertificates = array_map(static function (string $x509): string {
|
||||
return CertificateToolbox::fixPEMStructure($x509);
|
||||
}, $header['x5c']);
|
||||
$algorithm = new ES256();
|
||||
$isValid = $algorithm->verify($key, $signature->getEncodedProtectedHeader().'.'.$jws->getEncodedPayload(), $signature->getSignature());
|
||||
Assertion::true($isValid, 'Invalid response from the metadata service. The token signature is invalid.');
|
||||
|
||||
return $jws->getPayload();
|
||||
}
|
||||
}
|
|
@ -1,602 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use InvalidArgumentException;
|
||||
use JsonSerializable;
|
||||
use function Safe\json_decode;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class MetadataStatement implements JsonSerializable
|
||||
{
|
||||
public const KEY_PROTECTION_SOFTWARE = 0x0001;
|
||||
public const KEY_PROTECTION_HARDWARE = 0x0002;
|
||||
public const KEY_PROTECTION_TEE = 0x0004;
|
||||
public const KEY_PROTECTION_SECURE_ELEMENT = 0x0008;
|
||||
public const KEY_PROTECTION_REMOTE_HANDLE = 0x0010;
|
||||
|
||||
public const MATCHER_PROTECTION_SOFTWARE = 0x0001;
|
||||
public const MATCHER_PROTECTION_TEE = 0x0002;
|
||||
public const MATCHER_PROTECTION_ON_CHIP = 0x0004;
|
||||
|
||||
public const ATTACHMENT_HINT_INTERNAL = 0x0001;
|
||||
public const ATTACHMENT_HINT_EXTERNAL = 0x0002;
|
||||
public const ATTACHMENT_HINT_WIRED = 0x0004;
|
||||
public const ATTACHMENT_HINT_WIRELESS = 0x0008;
|
||||
public const ATTACHMENT_HINT_NFC = 0x0010;
|
||||
public const ATTACHMENT_HINT_BLUETOOTH = 0x0020;
|
||||
public const ATTACHMENT_HINT_NETWORK = 0x0040;
|
||||
public const ATTACHMENT_HINT_READY = 0x0080;
|
||||
public const ATTACHMENT_HINT_WIFI_DIRECT = 0x0100;
|
||||
|
||||
public const TRANSACTION_CONFIRMATION_DISPLAY_ANY = 0x0001;
|
||||
public const TRANSACTION_CONFIRMATION_DISPLAY_PRIVILEGED_SOFTWARE = 0x0002;
|
||||
public const TRANSACTION_CONFIRMATION_DISPLAY_TEE = 0x0004;
|
||||
public const TRANSACTION_CONFIRMATION_DISPLAY_HARDWARE = 0x0008;
|
||||
public const TRANSACTION_CONFIRMATION_DISPLAY_REMOTE = 0x0010;
|
||||
|
||||
public const ALG_SIGN_SECP256R1_ECDSA_SHA256_RAW = 0x0001;
|
||||
public const ALG_SIGN_SECP256R1_ECDSA_SHA256_DER = 0x0002;
|
||||
public const ALG_SIGN_RSASSA_PSS_SHA256_RAW = 0x0003;
|
||||
public const ALG_SIGN_RSASSA_PSS_SHA256_DER = 0x0004;
|
||||
public const ALG_SIGN_SECP256K1_ECDSA_SHA256_RAW = 0x0005;
|
||||
public const ALG_SIGN_SECP256K1_ECDSA_SHA256_DER = 0x0006;
|
||||
public const ALG_SIGN_SM2_SM3_RAW = 0x0007;
|
||||
public const ALG_SIGN_RSA_EMSA_PKCS1_SHA256_RAW = 0x0008;
|
||||
public const ALG_SIGN_RSA_EMSA_PKCS1_SHA256_DER = 0x0009;
|
||||
public const ALG_SIGN_RSASSA_PSS_SHA384_RAW = 0x000A;
|
||||
public const ALG_SIGN_RSASSA_PSS_SHA512_RAW = 0x000B;
|
||||
public const ALG_SIGN_RSASSA_PKCSV15_SHA256_RAW = 0x000C;
|
||||
public const ALG_SIGN_RSASSA_PKCSV15_SHA384_RAW = 0x000D;
|
||||
public const ALG_SIGN_RSASSA_PKCSV15_SHA512_RAW = 0x000E;
|
||||
public const ALG_SIGN_RSASSA_PKCSV15_SHA1_RAW = 0x000F;
|
||||
public const ALG_SIGN_SECP384R1_ECDSA_SHA384_RAW = 0x0010;
|
||||
public const ALG_SIGN_SECP521R1_ECDSA_SHA512_RAW = 0x0011;
|
||||
public const ALG_SIGN_ED25519_EDDSA_SHA256_RAW = 0x0012;
|
||||
|
||||
public const ALG_KEY_ECC_X962_RAW = 0x0100;
|
||||
public const ALG_KEY_ECC_X962_DER = 0x0101;
|
||||
public const ALG_KEY_RSA_2048_RAW = 0x0102;
|
||||
public const ALG_KEY_RSA_2048_DER = 0x0103;
|
||||
public const ALG_KEY_COSE = 0x0104;
|
||||
|
||||
public const ATTESTATION_BASIC_FULL = 0x3E07;
|
||||
public const ATTESTATION_BASIC_SURROGATE = 0x3E08;
|
||||
public const ATTESTATION_ECDAA = 0x3E09;
|
||||
public const ATTESTATION_ATTCA = 0x3E0A;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $legalHeader;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $aaid;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $aaguid;
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $attestationCertificateKeyIdentifiers = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $alternativeDescriptions = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $authenticatorVersion;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $protocolFamily;
|
||||
|
||||
/**
|
||||
* @var Version[]
|
||||
*/
|
||||
private $upv = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $assertionScheme;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $authenticationAlgorithm;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $authenticationAlgorithms = [];
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $publicKeyAlgAndEncoding;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $publicKeyAlgAndEncodings = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $attestationTypes = [];
|
||||
|
||||
/**
|
||||
* @var VerificationMethodANDCombinations[]
|
||||
*/
|
||||
private $userVerificationDetails = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $keyProtection;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private $isKeyRestricted;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private $isFreshUserVerificationRequired;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $matcherProtection;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $cryptoStrength;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $operatingEnv;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $attachmentHint = 0;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private $isSecondFactorOnly;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $tcDisplay;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $tcDisplayContentType;
|
||||
|
||||
/**
|
||||
* @var DisplayPNGCharacteristicsDescriptor[]
|
||||
*/
|
||||
private $tcDisplayPNGCharacteristics = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $attestationRootCertificates = [];
|
||||
|
||||
/**
|
||||
* @var EcdaaTrustAnchor[]
|
||||
*/
|
||||
private $ecdaaTrustAnchors = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $icon;
|
||||
|
||||
/**
|
||||
* @var ExtensionDescriptor[]
|
||||
*/
|
||||
private $supportedExtensions = [];
|
||||
|
||||
/**
|
||||
* @var array<int, StatusReport>
|
||||
*/
|
||||
private $statusReports = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $rootCertificates = [];
|
||||
|
||||
public static function createFromString(string $statement): self
|
||||
{
|
||||
$data = json_decode($statement, true);
|
||||
Assertion::isArray($data, 'Invalid Metadata Statement');
|
||||
|
||||
return self::createFromArray($data);
|
||||
}
|
||||
|
||||
public function getLegalHeader(): ?string
|
||||
{
|
||||
return $this->legalHeader;
|
||||
}
|
||||
|
||||
public function getAaid(): ?string
|
||||
{
|
||||
return $this->aaid;
|
||||
}
|
||||
|
||||
public function getAaguid(): ?string
|
||||
{
|
||||
return $this->aaguid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAttestationCertificateKeyIdentifiers(): array
|
||||
{
|
||||
return $this->attestationCertificateKeyIdentifiers;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAlternativeDescriptions(): array
|
||||
{
|
||||
return $this->alternativeDescriptions;
|
||||
}
|
||||
|
||||
public function getAuthenticatorVersion(): int
|
||||
{
|
||||
return $this->authenticatorVersion;
|
||||
}
|
||||
|
||||
public function getProtocolFamily(): string
|
||||
{
|
||||
return $this->protocolFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Version[]
|
||||
*/
|
||||
public function getUpv(): array
|
||||
{
|
||||
return $this->upv;
|
||||
}
|
||||
|
||||
public function getAssertionScheme(): ?string
|
||||
{
|
||||
return $this->assertionScheme;
|
||||
}
|
||||
|
||||
public function getAuthenticationAlgorithm(): ?int
|
||||
{
|
||||
return $this->authenticationAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getAuthenticationAlgorithms(): array
|
||||
{
|
||||
return $this->authenticationAlgorithms;
|
||||
}
|
||||
|
||||
public function getPublicKeyAlgAndEncoding(): ?int
|
||||
{
|
||||
return $this->publicKeyAlgAndEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getPublicKeyAlgAndEncodings(): array
|
||||
{
|
||||
return $this->publicKeyAlgAndEncodings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getAttestationTypes(): array
|
||||
{
|
||||
return $this->attestationTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return VerificationMethodANDCombinations[]
|
||||
*/
|
||||
public function getUserVerificationDetails(): array
|
||||
{
|
||||
return $this->userVerificationDetails;
|
||||
}
|
||||
|
||||
public function getKeyProtection(): int
|
||||
{
|
||||
return $this->keyProtection;
|
||||
}
|
||||
|
||||
public function isKeyRestricted(): ?bool
|
||||
{
|
||||
return (bool) $this->isKeyRestricted;
|
||||
}
|
||||
|
||||
public function isFreshUserVerificationRequired(): ?bool
|
||||
{
|
||||
return (bool) $this->isFreshUserVerificationRequired;
|
||||
}
|
||||
|
||||
public function getMatcherProtection(): int
|
||||
{
|
||||
return $this->matcherProtection;
|
||||
}
|
||||
|
||||
public function getCryptoStrength(): ?int
|
||||
{
|
||||
return $this->cryptoStrength;
|
||||
}
|
||||
|
||||
public function getOperatingEnv(): ?string
|
||||
{
|
||||
return $this->operatingEnv;
|
||||
}
|
||||
|
||||
public function getAttachmentHint(): int
|
||||
{
|
||||
return $this->attachmentHint;
|
||||
}
|
||||
|
||||
public function isSecondFactorOnly(): ?bool
|
||||
{
|
||||
return (bool) $this->isSecondFactorOnly;
|
||||
}
|
||||
|
||||
public function getTcDisplay(): int
|
||||
{
|
||||
return $this->tcDisplay;
|
||||
}
|
||||
|
||||
public function getTcDisplayContentType(): ?string
|
||||
{
|
||||
return $this->tcDisplayContentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DisplayPNGCharacteristicsDescriptor[]
|
||||
*/
|
||||
public function getTcDisplayPNGCharacteristics(): array
|
||||
{
|
||||
return $this->tcDisplayPNGCharacteristics;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAttestationRootCertificates(): array
|
||||
{
|
||||
return $this->attestationRootCertificates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EcdaaTrustAnchor[]
|
||||
*/
|
||||
public function getEcdaaTrustAnchors(): array
|
||||
{
|
||||
return $this->ecdaaTrustAnchors;
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
{
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ExtensionDescriptor[]
|
||||
*/
|
||||
public function getSupportedExtensions(): array
|
||||
{
|
||||
return $this->supportedExtensions;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$object = new self();
|
||||
foreach (['description', 'protocolFamily'] as $key) {
|
||||
if (!isset($data[$key])) {
|
||||
throw new InvalidArgumentException(sprintf('The parameter "%s" is missing', $key));
|
||||
}
|
||||
}
|
||||
$object->legalHeader = $data['legalHeader'] ?? null;
|
||||
$object->aaid = $data['aaid'] ?? null;
|
||||
$object->aaguid = $data['aaguid'] ?? null;
|
||||
$object->attestationCertificateKeyIdentifiers = $data['attestationCertificateKeyIdentifiers'] ?? [];
|
||||
$object->description = $data['description'];
|
||||
$object->alternativeDescriptions = $data['alternativeDescriptions'] ?? [];
|
||||
$object->authenticatorVersion = $data['authenticatorVersion'] ?? 0;
|
||||
$object->protocolFamily = $data['protocolFamily'];
|
||||
if (isset($data['upv'])) {
|
||||
$upv = $data['upv'];
|
||||
Assertion::isArray($upv, 'Invalid Metadata Statement');
|
||||
foreach ($upv as $value) {
|
||||
Assertion::isArray($value, 'Invalid Metadata Statement');
|
||||
$object->upv[] = Version::createFromArray($value);
|
||||
}
|
||||
}
|
||||
$object->assertionScheme = $data['assertionScheme'] ?? null;
|
||||
$object->authenticationAlgorithm = $data['authenticationAlgorithm'] ?? null;
|
||||
$object->authenticationAlgorithms = $data['authenticationAlgorithms'] ?? [];
|
||||
$object->publicKeyAlgAndEncoding = $data['publicKeyAlgAndEncoding'] ?? null;
|
||||
$object->publicKeyAlgAndEncodings = $data['publicKeyAlgAndEncodings'] ?? [];
|
||||
$object->attestationTypes = $data['attestationTypes'] ?? [];
|
||||
if (isset($data['userVerificationDetails'])) {
|
||||
$userVerificationDetails = $data['userVerificationDetails'];
|
||||
Assertion::isArray($userVerificationDetails, 'Invalid Metadata Statement');
|
||||
foreach ($userVerificationDetails as $value) {
|
||||
Assertion::isArray($value, 'Invalid Metadata Statement');
|
||||
$object->userVerificationDetails[] = VerificationMethodANDCombinations::createFromArray($value);
|
||||
}
|
||||
}
|
||||
$object->keyProtection = $data['keyProtection'] ?? 0;
|
||||
$object->isKeyRestricted = $data['isKeyRestricted'] ?? null;
|
||||
$object->isFreshUserVerificationRequired = $data['isFreshUserVerificationRequired'] ?? null;
|
||||
$object->matcherProtection = $data['matcherProtection'] ?? 0;
|
||||
$object->cryptoStrength = $data['cryptoStrength'] ?? null;
|
||||
$object->operatingEnv = $data['operatingEnv'] ?? null;
|
||||
$object->attachmentHint = $data['attachmentHint'] ?? 0;
|
||||
$object->isSecondFactorOnly = $data['isSecondFactorOnly'] ?? null;
|
||||
$object->tcDisplay = $data['tcDisplay'] ?? 0;
|
||||
$object->tcDisplayContentType = $data['tcDisplayContentType'] ?? null;
|
||||
if (isset($data['tcDisplayPNGCharacteristics'])) {
|
||||
$tcDisplayPNGCharacteristics = $data['tcDisplayPNGCharacteristics'];
|
||||
Assertion::isArray($tcDisplayPNGCharacteristics, 'Invalid Metadata Statement');
|
||||
foreach ($tcDisplayPNGCharacteristics as $tcDisplayPNGCharacteristic) {
|
||||
Assertion::isArray($tcDisplayPNGCharacteristic, 'Invalid Metadata Statement');
|
||||
$object->tcDisplayPNGCharacteristics[] = DisplayPNGCharacteristicsDescriptor::createFromArray($tcDisplayPNGCharacteristic);
|
||||
}
|
||||
}
|
||||
$object->attestationRootCertificates = $data['attestationRootCertificates'] ?? [];
|
||||
$object->ecdaaTrustAnchors = $data['ecdaaTrustAnchors'] ?? [];
|
||||
$object->icon = $data['icon'] ?? null;
|
||||
if (isset($data['supportedExtensions'])) {
|
||||
$supportedExtensions = $data['supportedExtensions'];
|
||||
Assertion::isArray($supportedExtensions, 'Invalid Metadata Statement');
|
||||
foreach ($supportedExtensions as $supportedExtension) {
|
||||
Assertion::isArray($supportedExtension, 'Invalid Metadata Statement');
|
||||
$object->supportedExtensions[] = ExtensionDescriptor::createFromArray($supportedExtension);
|
||||
}
|
||||
}
|
||||
$object->rootCertificates = $data['rootCertificates'] ?? [];
|
||||
if (isset($data['statusReports'])) {
|
||||
$reports = $data['statusReports'];
|
||||
Assertion::isArray($reports, 'Invalid Metadata Statement');
|
||||
foreach ($reports as $report) {
|
||||
Assertion::isArray($report, 'Invalid Metadata Statement');
|
||||
$object->statusReports[] = StatusReport::createFromArray($report);
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'legalHeader' => $this->legalHeader,
|
||||
'aaid' => $this->aaid,
|
||||
'aaguid' => $this->aaguid,
|
||||
'attestationCertificateKeyIdentifiers' => $this->attestationCertificateKeyIdentifiers,
|
||||
'description' => $this->description,
|
||||
'alternativeDescriptions' => $this->alternativeDescriptions,
|
||||
'authenticatorVersion' => $this->authenticatorVersion,
|
||||
'protocolFamily' => $this->protocolFamily,
|
||||
'upv' => $this->upv,
|
||||
'assertionScheme' => $this->assertionScheme,
|
||||
'authenticationAlgorithm' => $this->authenticationAlgorithm,
|
||||
'authenticationAlgorithms' => $this->authenticationAlgorithms,
|
||||
'publicKeyAlgAndEncoding' => $this->publicKeyAlgAndEncoding,
|
||||
'publicKeyAlgAndEncodings' => $this->publicKeyAlgAndEncodings,
|
||||
'attestationTypes' => $this->attestationTypes,
|
||||
'userVerificationDetails' => $this->userVerificationDetails,
|
||||
'keyProtection' => $this->keyProtection,
|
||||
'isKeyRestricted' => $this->isKeyRestricted,
|
||||
'isFreshUserVerificationRequired' => $this->isFreshUserVerificationRequired,
|
||||
'matcherProtection' => $this->matcherProtection,
|
||||
'cryptoStrength' => $this->cryptoStrength,
|
||||
'operatingEnv' => $this->operatingEnv,
|
||||
'attachmentHint' => $this->attachmentHint,
|
||||
'isSecondFactorOnly' => $this->isSecondFactorOnly,
|
||||
'tcDisplay' => $this->tcDisplay,
|
||||
'tcDisplayContentType' => $this->tcDisplayContentType,
|
||||
'tcDisplayPNGCharacteristics' => array_map(static function (DisplayPNGCharacteristicsDescriptor $object): array {
|
||||
return $object->jsonSerialize();
|
||||
}, $this->tcDisplayPNGCharacteristics),
|
||||
'attestationRootCertificates' => $this->attestationRootCertificates,
|
||||
'ecdaaTrustAnchors' => array_map(static function (EcdaaTrustAnchor $object): array {
|
||||
return $object->jsonSerialize();
|
||||
}, $this->ecdaaTrustAnchors),
|
||||
'icon' => $this->icon,
|
||||
'supportedExtensions' => array_map(static function (ExtensionDescriptor $object): array {
|
||||
return $object->jsonSerialize();
|
||||
}, $this->supportedExtensions),
|
||||
'rootCertificates' => $this->rootCertificates,
|
||||
'statusReports' => $this->statusReports,
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StatusReport[]
|
||||
*/
|
||||
public function getStatusReports(): array
|
||||
{
|
||||
return $this->statusReports;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StatusReport[] $statusReports
|
||||
*/
|
||||
public function setStatusReports(array $statusReports): self
|
||||
{
|
||||
$this->statusReports = $statusReports;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getRootCertificates(): array
|
||||
{
|
||||
return $this->rootCertificates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $rootCertificates
|
||||
*/
|
||||
public function setRootCertificates(array $rootCertificates): self
|
||||
{
|
||||
$this->rootCertificates = $rootCertificates;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use Base64Url\Base64Url;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Jose\Component\Signature\Algorithm\ES256;
|
||||
use Jose\Component\Signature\Serializer\CompactSerializer;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use function Safe\json_decode;
|
||||
use function Safe\sprintf;
|
||||
|
||||
/**
|
||||
* @deprecated This class is deprecated since v3.3 and will be removed in v4.0
|
||||
*/
|
||||
class MetadataStatementFetcher
|
||||
{
|
||||
public static function fetchTableOfContent(string $uri, ClientInterface $client, RequestFactoryInterface $requestFactory, array $additionalHeaders = []): MetadataTOCPayload
|
||||
{
|
||||
$content = self::fetch($uri, $client, $requestFactory, $additionalHeaders);
|
||||
$payload = self::getJwsPayload($content);
|
||||
$data = json_decode($payload, true);
|
||||
|
||||
return MetadataTOCPayload::createFromArray($data);
|
||||
}
|
||||
|
||||
public static function fetchMetadataStatement(string $uri, bool $isBase64UrlEncoded, ClientInterface $client, RequestFactoryInterface $requestFactory, array $additionalHeaders = [], string $hash = '', string $hashingFunction = 'sha256'): MetadataStatement
|
||||
{
|
||||
$payload = self::fetch($uri, $client, $requestFactory, $additionalHeaders);
|
||||
if ('' !== $hash) {
|
||||
Assertion::true(hash_equals($hash, hash($hashingFunction, $payload, true)), 'The hash cannot be verified. The metadata statement shall be rejected');
|
||||
}
|
||||
$json = $isBase64UrlEncoded ? Base64Url::decode($payload) : $payload;
|
||||
$data = json_decode($json, true);
|
||||
|
||||
return MetadataStatement::createFromArray($data);
|
||||
}
|
||||
|
||||
private static function fetch(string $uri, ClientInterface $client, RequestFactoryInterface $requestFactory, array $additionalHeaders = []): string
|
||||
{
|
||||
$request = $requestFactory->createRequest('GET', $uri);
|
||||
foreach ($additionalHeaders as $k => $v) {
|
||||
$request = $request->withHeader($k, $v);
|
||||
}
|
||||
$response = $client->sendRequest($request);
|
||||
Assertion::eq(200, $response->getStatusCode(), sprintf('Unable to contact the server. Response code is %d', $response->getStatusCode()));
|
||||
$content = $response->getBody()->getContents();
|
||||
Assertion::notEmpty($content, 'Unable to contact the server. The response has no content');
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private static function getJwsPayload(string $token): string
|
||||
{
|
||||
$jws = (new CompactSerializer())->unserialize($token);
|
||||
Assertion::eq(1, $jws->countSignatures(), 'Invalid response from the metadata service. Only one signature shall be present.');
|
||||
$signature = $jws->getSignature(0);
|
||||
$payload = $jws->getPayload();
|
||||
Assertion::notEmpty($payload, 'Invalid response from the metadata service. The token payload is empty.');
|
||||
$header = $signature->getProtectedHeader();
|
||||
Assertion::keyExists($header, 'alg', 'The "alg" parameter is missing.');
|
||||
Assertion::eq($header['alg'], 'ES256', 'The expected "alg" parameter value should be "ES256".');
|
||||
Assertion::keyExists($header, 'x5c', 'The "x5c" parameter is missing.');
|
||||
Assertion::isArray($header['x5c'], 'The "x5c" parameter should be an array.');
|
||||
$key = JWKFactory::createFromX5C($header['x5c']);
|
||||
$algorithm = new ES256();
|
||||
$isValid = $algorithm->verify($key, $signature->getEncodedProtectedHeader().'.'.$jws->getEncodedPayload(), $signature->getSignature());
|
||||
Assertion::true($isValid, 'Invalid response from the metadata service. The token signature is invalid.');
|
||||
|
||||
return $jws->getPayload();
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
interface MetadataStatementRepository
|
||||
{
|
||||
public function findOneByAAGUID(string $aaguid): ?MetadataStatement;
|
||||
|
||||
/**
|
||||
* @deprecated This method is deprecated since v3.3 and will be removed in v4.0. Please use the method "getStatusReports()" provided by the MetadataStatement object
|
||||
*
|
||||
* @return StatusReport[]
|
||||
*/
|
||||
public function findStatusReportsByAAGUID(string $aaguid): array;
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use function array_key_exists;
|
||||
use Assert\Assertion;
|
||||
use JsonSerializable;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class MetadataTOCPayload implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $legalHeader;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $no;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $nextUpdate;
|
||||
|
||||
/**
|
||||
* @var MetadataTOCPayloadEntry[]
|
||||
*/
|
||||
private $entries = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $rootCertificates;
|
||||
|
||||
public function __construct(int $no, string $nextUpdate, ?string $legalHeader = null)
|
||||
{
|
||||
$this->no = $no;
|
||||
$this->nextUpdate = $nextUpdate;
|
||||
$this->legalHeader = $legalHeader;
|
||||
}
|
||||
|
||||
public function addEntry(MetadataTOCPayloadEntry $entry): self
|
||||
{
|
||||
$this->entries[] = $entry;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLegalHeader(): ?string
|
||||
{
|
||||
return $this->legalHeader;
|
||||
}
|
||||
|
||||
public function getNo(): int
|
||||
{
|
||||
return $this->no;
|
||||
}
|
||||
|
||||
public function getNextUpdate(): string
|
||||
{
|
||||
return $this->nextUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MetadataTOCPayloadEntry[]
|
||||
*/
|
||||
public function getEntries(): array
|
||||
{
|
||||
return $this->entries;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$data = Utils::filterNullValues($data);
|
||||
foreach (['no', 'nextUpdate', 'entries'] as $key) {
|
||||
Assertion::keyExists($data, $key, Utils::logicException(sprintf('Invalid data. The parameter "%s" is missing', $key)));
|
||||
}
|
||||
Assertion::integer($data['no'], Utils::logicException('Invalid data. The parameter "no" shall be an integer'));
|
||||
Assertion::string($data['nextUpdate'], Utils::logicException('Invalid data. The parameter "nextUpdate" shall be a string'));
|
||||
Assertion::isArray($data['entries'], Utils::logicException('Invalid data. The parameter "entries" shall be a n array of entries'));
|
||||
if (array_key_exists('legalHeader', $data)) {
|
||||
Assertion::string($data['legalHeader'], Utils::logicException('Invalid data. The parameter "legalHeader" shall be a string'));
|
||||
}
|
||||
$object = new self(
|
||||
$data['no'],
|
||||
$data['nextUpdate'],
|
||||
$data['legalHeader'] ?? null
|
||||
);
|
||||
foreach ($data['entries'] as $k => $entry) {
|
||||
$object->addEntry(MetadataTOCPayloadEntry::createFromArray($entry));
|
||||
}
|
||||
$object->rootCertificates = $data['rootCertificates'] ?? [];
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'legalHeader' => $this->legalHeader,
|
||||
'nextUpdate' => $this->nextUpdate,
|
||||
'no' => $this->no,
|
||||
'entries' => array_map(static function (MetadataTOCPayloadEntry $object): array {
|
||||
return $object->jsonSerialize();
|
||||
}, $this->entries),
|
||||
'rootCertificates' => $this->rootCertificates,
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getRootCertificates(): array
|
||||
{
|
||||
return $this->rootCertificates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $rootCertificates
|
||||
*/
|
||||
public function setRootCertificates(array $rootCertificates): self
|
||||
{
|
||||
$this->rootCertificates = $rootCertificates;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use Base64Url\Base64Url;
|
||||
use function count;
|
||||
use JsonSerializable;
|
||||
use LogicException;
|
||||
|
||||
class MetadataTOCPayloadEntry implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $aaid;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $aaguid;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $attestationCertificateKeyIdentifiers = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $hash;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var StatusReport[]
|
||||
*/
|
||||
private $statusReports = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $timeOfLastStatusChange;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $rogueListURL;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $rogueListHash;
|
||||
|
||||
public function __construct(?string $aaid, ?string $aaguid, array $attestationCertificateKeyIdentifiers, ?string $hash, ?string $url, string $timeOfLastStatusChange, ?string $rogueListURL, ?string $rogueListHash)
|
||||
{
|
||||
if (null !== $aaid && null !== $aaguid) {
|
||||
throw new LogicException('Authenticators cannot support both AAID and AAGUID');
|
||||
}
|
||||
if (null === $aaid && null === $aaguid && 0 === count($attestationCertificateKeyIdentifiers)) {
|
||||
throw new LogicException('If neither AAID nor AAGUID are set, the attestation certificate identifier list shall not be empty');
|
||||
}
|
||||
foreach ($attestationCertificateKeyIdentifiers as $attestationCertificateKeyIdentifier) {
|
||||
Assertion::string($attestationCertificateKeyIdentifier, Utils::logicException('Invalid attestation certificate identifier. Shall be a list of strings'));
|
||||
Assertion::notEmpty($attestationCertificateKeyIdentifier, Utils::logicException('Invalid attestation certificate identifier. Shall be a list of strings'));
|
||||
Assertion::regex($attestationCertificateKeyIdentifier, '/^[0-9a-f]+$/', Utils::logicException('Invalid attestation certificate identifier. Shall be a list of strings'));
|
||||
}
|
||||
$this->aaid = $aaid;
|
||||
$this->aaguid = $aaguid;
|
||||
$this->attestationCertificateKeyIdentifiers = $attestationCertificateKeyIdentifiers;
|
||||
$this->hash = Base64Url::decode($hash);
|
||||
$this->url = $url;
|
||||
$this->timeOfLastStatusChange = $timeOfLastStatusChange;
|
||||
$this->rogueListURL = $rogueListURL;
|
||||
$this->rogueListHash = $rogueListHash;
|
||||
}
|
||||
|
||||
public function getAaid(): ?string
|
||||
{
|
||||
return $this->aaid;
|
||||
}
|
||||
|
||||
public function getAaguid(): ?string
|
||||
{
|
||||
return $this->aaguid;
|
||||
}
|
||||
|
||||
public function getAttestationCertificateKeyIdentifiers(): array
|
||||
{
|
||||
return $this->attestationCertificateKeyIdentifiers;
|
||||
}
|
||||
|
||||
public function getHash(): ?string
|
||||
{
|
||||
return $this->hash;
|
||||
}
|
||||
|
||||
public function getUrl(): ?string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function addStatusReports(StatusReport $statusReport): self
|
||||
{
|
||||
$this->statusReports[] = $statusReport;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StatusReport[]
|
||||
*/
|
||||
public function getStatusReports(): array
|
||||
{
|
||||
return $this->statusReports;
|
||||
}
|
||||
|
||||
public function getTimeOfLastStatusChange(): string
|
||||
{
|
||||
return $this->timeOfLastStatusChange;
|
||||
}
|
||||
|
||||
public function getRogueListURL(): string
|
||||
{
|
||||
return $this->rogueListURL;
|
||||
}
|
||||
|
||||
public function getRogueListHash(): string
|
||||
{
|
||||
return $this->rogueListHash;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$data = Utils::filterNullValues($data);
|
||||
Assertion::keyExists($data, 'timeOfLastStatusChange', Utils::logicException('Invalid data. The parameter "timeOfLastStatusChange" is missing'));
|
||||
Assertion::keyExists($data, 'statusReports', Utils::logicException('Invalid data. The parameter "statusReports" is missing'));
|
||||
Assertion::isArray($data['statusReports'], Utils::logicException('Invalid data. The parameter "statusReports" shall be an array of StatusReport objects'));
|
||||
$object = new self(
|
||||
$data['aaid'] ?? null,
|
||||
$data['aaguid'] ?? null,
|
||||
$data['attestationCertificateKeyIdentifiers'] ?? [],
|
||||
$data['hash'] ?? null,
|
||||
$data['url'] ?? null,
|
||||
$data['timeOfLastStatusChange'],
|
||||
$data['rogueListURL'] ?? null,
|
||||
$data['rogueListHash'] ?? null
|
||||
);
|
||||
foreach ($data['statusReports'] as $statusReport) {
|
||||
$object->addStatusReports(StatusReport::createFromArray($statusReport));
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'aaid' => $this->aaid,
|
||||
'aaguid' => $this->aaguid,
|
||||
'attestationCertificateKeyIdentifiers' => $this->attestationCertificateKeyIdentifiers,
|
||||
'hash' => Base64Url::encode($this->hash),
|
||||
'url' => $this->url,
|
||||
'statusReports' => array_map(static function (StatusReport $object): array {
|
||||
return $object->jsonSerialize();
|
||||
}, $this->statusReports),
|
||||
'timeOfLastStatusChange' => $this->timeOfLastStatusChange,
|
||||
'rogueListURL' => $this->rogueListURL,
|
||||
'rogueListHash' => $this->rogueListHash,
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use function array_key_exists;
|
||||
use Assert\Assertion;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class PatternAccuracyDescriptor extends AbstractDescriptor
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $minComplexity;
|
||||
|
||||
public function __construct(int $minComplexity, ?int $maxRetries = null, ?int $blockSlowdown = null)
|
||||
{
|
||||
Assertion::greaterOrEqualThan($minComplexity, 0, Utils::logicException('Invalid data. The value of "minComplexity" must be a positive integer'));
|
||||
$this->minComplexity = $minComplexity;
|
||||
parent::__construct($maxRetries, $blockSlowdown);
|
||||
}
|
||||
|
||||
public function getMinComplexity(): int
|
||||
{
|
||||
return $this->minComplexity;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$data = Utils::filterNullValues($data);
|
||||
Assertion::keyExists($data, 'minComplexity', Utils::logicException('The key "minComplexity" is missing'));
|
||||
foreach (['minComplexity', 'maxRetries', 'blockSlowdown'] as $key) {
|
||||
if (array_key_exists($key, $data)) {
|
||||
Assertion::integer($data[$key], Utils::logicException(sprintf('Invalid data. The value of "%s" must be a positive integer', $key)));
|
||||
}
|
||||
}
|
||||
|
||||
return new self(
|
||||
$data['minComplexity'],
|
||||
$data['maxRetries'] ?? null,
|
||||
$data['blockSlowdown'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'minComplexity' => $this->minComplexity,
|
||||
'maxRetries' => $this->getMaxRetries(),
|
||||
'blockSlowdown' => $this->getBlockSlowdown(),
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use JsonSerializable;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class RgbPaletteEntry implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $r;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $g;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $b;
|
||||
|
||||
public function __construct(int $r, int $g, int $b)
|
||||
{
|
||||
Assertion::range($r, 0, 255, Utils::logicException('The key "r" is invalid'));
|
||||
Assertion::range($g, 0, 255, Utils::logicException('The key "g" is invalid'));
|
||||
Assertion::range($b, 0, 255, Utils::logicException('The key "b" is invalid'));
|
||||
$this->r = $r;
|
||||
$this->g = $g;
|
||||
$this->b = $b;
|
||||
}
|
||||
|
||||
public function getR(): int
|
||||
{
|
||||
return $this->r;
|
||||
}
|
||||
|
||||
public function getG(): int
|
||||
{
|
||||
return $this->g;
|
||||
}
|
||||
|
||||
public function getB(): int
|
||||
{
|
||||
return $this->b;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
foreach (['r', 'g', 'b'] as $key) {
|
||||
Assertion::keyExists($data, $key, sprintf('The key "%s" is missing', $key));
|
||||
Assertion::integer($data[$key], sprintf('The key "%s" is invalid', $key));
|
||||
}
|
||||
|
||||
return new self(
|
||||
$data['r'],
|
||||
$data['g'],
|
||||
$data['b']
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'r' => $this->r,
|
||||
'g' => $this->g,
|
||||
'b' => $this->b,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use JsonSerializable;
|
||||
|
||||
class RogueListEntry implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sk;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $date;
|
||||
|
||||
public function __construct(string $sk, string $date)
|
||||
{
|
||||
$this->sk = $sk;
|
||||
$this->date = $date;
|
||||
}
|
||||
|
||||
public function getSk(): string
|
||||
{
|
||||
return $this->sk;
|
||||
}
|
||||
|
||||
public function getDate(): ?string
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
Assertion::keyExists($data, 'sk', 'The key "sk" is missing');
|
||||
Assertion::string($data['sk'], 'The key "sk" is invalid');
|
||||
Assertion::keyExists($data, 'date', 'The key "date" is missing');
|
||||
Assertion::string($data['date'], 'The key "date" is invalid');
|
||||
|
||||
return new self(
|
||||
$data['sk'],
|
||||
$data['date']
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'sk' => $this->sk,
|
||||
'date' => $this->date,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use function Safe\base64_decode;
|
||||
use function Safe\json_decode;
|
||||
|
||||
class SingleMetadata
|
||||
{
|
||||
/**
|
||||
* @var MetadataStatement
|
||||
*/
|
||||
private $statement;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $data;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isBase64Encoded;
|
||||
|
||||
public function __construct(string $data, bool $isBase64Encoded)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->isBase64Encoded = $isBase64Encoded;
|
||||
}
|
||||
|
||||
public function getMetadataStatement(): MetadataStatement
|
||||
{
|
||||
if (null === $this->statement) {
|
||||
$json = $this->data;
|
||||
if ($this->isBase64Encoded) {
|
||||
$json = base64_decode($this->data, true);
|
||||
}
|
||||
$statement = json_decode($json, true);
|
||||
$this->statement = MetadataStatement::createFromArray($statement);
|
||||
}
|
||||
|
||||
return $this->statement;
|
||||
}
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use function in_array;
|
||||
use JsonSerializable;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class StatusReport implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @see AuthenticatorStatus
|
||||
*/
|
||||
private $status;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $effectiveDate;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $certificate;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $certificationDescriptor;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $certificateNumber;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $certificationPolicyVersion;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $certificationRequirementsVersion;
|
||||
|
||||
public function __construct(string $status, ?string $effectiveDate, ?string $certificate, ?string $url, ?string $certificationDescriptor, ?string $certificateNumber, ?string $certificationPolicyVersion, ?string $certificationRequirementsVersion)
|
||||
{
|
||||
Assertion::inArray($status, AuthenticatorStatus::list(), Utils::logicException('The value of the key "status" is not acceptable'));
|
||||
|
||||
$this->status = $status;
|
||||
$this->effectiveDate = $effectiveDate;
|
||||
$this->certificate = $certificate;
|
||||
$this->url = $url;
|
||||
$this->certificationDescriptor = $certificationDescriptor;
|
||||
$this->certificateNumber = $certificateNumber;
|
||||
$this->certificationPolicyVersion = $certificationPolicyVersion;
|
||||
$this->certificationRequirementsVersion = $certificationRequirementsVersion;
|
||||
}
|
||||
|
||||
public function isCompromised(): bool
|
||||
{
|
||||
return in_array($this->status, [
|
||||
AuthenticatorStatus::ATTESTATION_KEY_COMPROMISE,
|
||||
AuthenticatorStatus::USER_KEY_PHYSICAL_COMPROMISE,
|
||||
AuthenticatorStatus::USER_KEY_REMOTE_COMPROMISE,
|
||||
AuthenticatorStatus::USER_VERIFICATION_BYPASS,
|
||||
], true);
|
||||
}
|
||||
|
||||
public function getStatus(): string
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function getEffectiveDate(): ?string
|
||||
{
|
||||
return $this->effectiveDate;
|
||||
}
|
||||
|
||||
public function getCertificate(): ?string
|
||||
{
|
||||
return $this->certificate;
|
||||
}
|
||||
|
||||
public function getUrl(): ?string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function getCertificationDescriptor(): ?string
|
||||
{
|
||||
return $this->certificationDescriptor;
|
||||
}
|
||||
|
||||
public function getCertificateNumber(): ?string
|
||||
{
|
||||
return $this->certificateNumber;
|
||||
}
|
||||
|
||||
public function getCertificationPolicyVersion(): ?string
|
||||
{
|
||||
return $this->certificationPolicyVersion;
|
||||
}
|
||||
|
||||
public function getCertificationRequirementsVersion(): ?string
|
||||
{
|
||||
return $this->certificationRequirementsVersion;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$data = Utils::filterNullValues($data);
|
||||
Assertion::keyExists($data, 'status', Utils::logicException('The key "status" is missing'));
|
||||
foreach (['effectiveDate', 'certificate', 'url', 'certificationDescriptor', 'certificateNumber', 'certificationPolicyVersion', 'certificationRequirementsVersion'] as $key) {
|
||||
if (isset($data[$key])) {
|
||||
Assertion::nullOrString($data[$key], Utils::logicException(sprintf('The value of the key "%s" is invalid', $key)));
|
||||
}
|
||||
}
|
||||
|
||||
return new self(
|
||||
$data['status'],
|
||||
$data['effectiveDate'] ?? null,
|
||||
$data['certificate'] ?? null,
|
||||
$data['url'] ?? null,
|
||||
$data['certificationDescriptor'] ?? null,
|
||||
$data['certificateNumber'] ?? null,
|
||||
$data['certificationPolicyVersion'] ?? null,
|
||||
$data['certificationRequirementsVersion'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'status' => $this->status,
|
||||
'effectiveDate' => $this->effectiveDate,
|
||||
'certificate' => $this->certificate,
|
||||
'url' => $this->url,
|
||||
'certificationDescriptor' => $this->certificationDescriptor,
|
||||
'certificateNumber' => $this->certificateNumber,
|
||||
'certificationPolicyVersion' => $this->certificationPolicyVersion,
|
||||
'certificationRequirementsVersion' => $this->certificationRequirementsVersion,
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use LogicException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
abstract class Utils
|
||||
{
|
||||
public static function logicException(string $message, ?Throwable $previousException = null): callable
|
||||
{
|
||||
return static function () use ($message, $previousException): LogicException {
|
||||
return new LogicException($message, 0, $previousException);
|
||||
};
|
||||
}
|
||||
|
||||
public static function filterNullValues(array $data): array
|
||||
{
|
||||
return array_filter($data, static function ($var): bool {return null !== $var; });
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use JsonSerializable;
|
||||
|
||||
class VerificationMethodANDCombinations implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var VerificationMethodDescriptor[]
|
||||
*/
|
||||
private $verificationMethods = [];
|
||||
|
||||
public function addVerificationMethodDescriptor(VerificationMethodDescriptor $verificationMethodDescriptor): self
|
||||
{
|
||||
$this->verificationMethods[] = $verificationMethodDescriptor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return VerificationMethodDescriptor[]
|
||||
*/
|
||||
public function getVerificationMethods(): array
|
||||
{
|
||||
return $this->verificationMethods;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$object = new self();
|
||||
|
||||
foreach ($data as $datum) {
|
||||
Assertion::isArray($datum, Utils::logicException('Invalid data'));
|
||||
$object->addVerificationMethodDescriptor(VerificationMethodDescriptor::createFromArray($datum));
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return array_map(static function (VerificationMethodDescriptor $object): array {
|
||||
return $object->jsonSerialize();
|
||||
}, $this->verificationMethods);
|
||||
}
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use Assert\Assertion;
|
||||
use JsonSerializable;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class VerificationMethodDescriptor implements JsonSerializable
|
||||
{
|
||||
public const USER_VERIFY_PRESENCE = 0x00000001;
|
||||
public const USER_VERIFY_FINGERPRINT = 0x00000002;
|
||||
public const USER_VERIFY_PASSCODE = 0x00000004;
|
||||
public const USER_VERIFY_VOICEPRINT = 0x00000008;
|
||||
public const USER_VERIFY_FACEPRINT = 0x00000010;
|
||||
public const USER_VERIFY_LOCATION = 0x00000020;
|
||||
public const USER_VERIFY_EYEPRINT = 0x00000040;
|
||||
public const USER_VERIFY_PATTERN = 0x00000080;
|
||||
public const USER_VERIFY_HANDPRINT = 0x00000100;
|
||||
public const USER_VERIFY_NONE = 0x00000200;
|
||||
public const USER_VERIFY_ALL = 0x00000400;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $userVerification;
|
||||
|
||||
/**
|
||||
* @var CodeAccuracyDescriptor|null
|
||||
*/
|
||||
private $caDesc;
|
||||
|
||||
/**
|
||||
* @var BiometricAccuracyDescriptor|null
|
||||
*/
|
||||
private $baDesc;
|
||||
|
||||
/**
|
||||
* @var PatternAccuracyDescriptor|null
|
||||
*/
|
||||
private $paDesc;
|
||||
|
||||
public function __construct(int $userVerification, ?CodeAccuracyDescriptor $caDesc = null, ?BiometricAccuracyDescriptor $baDesc = null, ?PatternAccuracyDescriptor $paDesc = null)
|
||||
{
|
||||
Assertion::greaterOrEqualThan($userVerification, 0, Utils::logicException('The parameter "userVerification" is invalid'));
|
||||
$this->userVerification = $userVerification;
|
||||
$this->caDesc = $caDesc;
|
||||
$this->baDesc = $baDesc;
|
||||
$this->paDesc = $paDesc;
|
||||
}
|
||||
|
||||
public function getUserVerification(): int
|
||||
{
|
||||
return $this->userVerification;
|
||||
}
|
||||
|
||||
public function userPresence(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_PRESENCE);
|
||||
}
|
||||
|
||||
public function fingerprint(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_FINGERPRINT);
|
||||
}
|
||||
|
||||
public function passcode(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_PASSCODE);
|
||||
}
|
||||
|
||||
public function voicePrint(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_VOICEPRINT);
|
||||
}
|
||||
|
||||
public function facePrint(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_FACEPRINT);
|
||||
}
|
||||
|
||||
public function location(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_LOCATION);
|
||||
}
|
||||
|
||||
public function eyePrint(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_EYEPRINT);
|
||||
}
|
||||
|
||||
public function pattern(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_PATTERN);
|
||||
}
|
||||
|
||||
public function handprint(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_HANDPRINT);
|
||||
}
|
||||
|
||||
public function none(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_NONE);
|
||||
}
|
||||
|
||||
public function all(): bool
|
||||
{
|
||||
return 0 !== ($this->userVerification & self::USER_VERIFY_ALL);
|
||||
}
|
||||
|
||||
public function getCaDesc(): ?CodeAccuracyDescriptor
|
||||
{
|
||||
return $this->caDesc;
|
||||
}
|
||||
|
||||
public function getBaDesc(): ?BiometricAccuracyDescriptor
|
||||
{
|
||||
return $this->baDesc;
|
||||
}
|
||||
|
||||
public function getPaDesc(): ?PatternAccuracyDescriptor
|
||||
{
|
||||
return $this->paDesc;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$data = Utils::filterNullValues($data);
|
||||
Assertion::keyExists($data, 'userVerification', Utils::logicException('The parameter "userVerification" is missing'));
|
||||
Assertion::integer($data['userVerification'], Utils::logicException('The parameter "userVerification" is invalid'));
|
||||
foreach (['caDesc', 'baDesc', 'paDesc'] as $key) {
|
||||
if (isset($data[$key])) {
|
||||
Assertion::isArray($data[$key], Utils::logicException(sprintf('Invalid parameter "%s"', $key)));
|
||||
}
|
||||
}
|
||||
|
||||
return new self(
|
||||
$data['userVerification'],
|
||||
isset($data['caDesc']) ? CodeAccuracyDescriptor::createFromArray($data['caDesc']) : null,
|
||||
isset($data['baDesc']) ? BiometricAccuracyDescriptor::createFromArray($data['baDesc']) : null,
|
||||
isset($data['paDesc']) ? PatternAccuracyDescriptor::createFromArray($data['paDesc']) : null
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'userVerification' => $this->userVerification,
|
||||
'caDesc' => null === $this->caDesc ? null : $this->caDesc->jsonSerialize(),
|
||||
'baDesc' => null === $this->baDesc ? null : $this->baDesc->jsonSerialize(),
|
||||
'paDesc' => null === $this->paDesc ? null : $this->paDesc->jsonSerialize(),
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Webauthn\MetadataService;
|
||||
|
||||
use function array_key_exists;
|
||||
use Assert\Assertion;
|
||||
use JsonSerializable;
|
||||
use LogicException;
|
||||
use function Safe\sprintf;
|
||||
|
||||
class Version implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $major;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $minor;
|
||||
|
||||
public function __construct(?int $major, ?int $minor)
|
||||
{
|
||||
if (null === $major && null === $minor) {
|
||||
throw new LogicException('Invalid data. Must contain at least one item');
|
||||
}
|
||||
Assertion::greaterOrEqualThan($major, 0, Utils::logicException('Invalid argument "major"'));
|
||||
Assertion::greaterOrEqualThan($minor, 0, Utils::logicException('Invalid argument "minor"'));
|
||||
|
||||
$this->major = $major;
|
||||
$this->minor = $minor;
|
||||
}
|
||||
|
||||
public function getMajor(): ?int
|
||||
{
|
||||
return $this->major;
|
||||
}
|
||||
|
||||
public function getMinor(): ?int
|
||||
{
|
||||
return $this->minor;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$data = Utils::filterNullValues($data);
|
||||
foreach (['major', 'minor'] as $key) {
|
||||
if (array_key_exists($key, $data)) {
|
||||
Assertion::integer($data[$key], sprintf('Invalid value for key "%s"', $key));
|
||||
}
|
||||
}
|
||||
|
||||
return new self(
|
||||
$data['major'] ?? null,
|
||||
$data['minor'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$data = [
|
||||
'major' => $this->major,
|
||||
'minor' => $this->minor,
|
||||
];
|
||||
|
||||
return Utils::filterNullValues($data);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue