migrating from u2f-api.js to webauthn

This commit is contained in:
FreddlePat
2022-01-12 21:09:18 +01:00
parent e2b4b6f6bc
commit d1d134038f
42 changed files with 918 additions and 461 deletions

View File

@@ -1,9 +1,9 @@
<?php
namespace WebAuthn\Attestation;
use WebAuthn\WebAuthnException;
use WebAuthn\CBOR\CborDecoder;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn\Attestation;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\CBOR\CborDecoder;
use lbuchs\WebAuthn\Binary\ByteBuffer;
/**
* @author Lukas Buchs
@@ -12,6 +12,7 @@ use WebAuthn\Binary\ByteBuffer;
class AttestationObject {
private $_authenticatorData;
private $_attestationFormat;
private $_attestationFormatName;
public function __construct($binary , $allowedFormats) {
$enc = CborDecoder::decode($binary);
@@ -29,13 +30,15 @@ class AttestationObject {
}
$this->_authenticatorData = new AuthenticatorData($enc['authData']->getBinaryString());
$this->_attestationFormatName = $enc['fmt'];
// Format ok?
if (!in_array($enc['fmt'], $allowedFormats)) {
throw new WebAuthnException('invalid atttestation format: ' . $enc['fmt'], WebAuthnException::INVALID_DATA);
if (!in_array($this->_attestationFormatName, $allowedFormats)) {
throw new WebAuthnException('invalid atttestation format: ' . $this->_attestationFormatName, WebAuthnException::INVALID_DATA);
}
switch ($enc['fmt']) {
switch ($this->_attestationFormatName) {
case 'android-key': $this->_attestationFormat = new Format\AndroidKey($enc, $this->_authenticatorData); break;
case 'android-safetynet': $this->_attestationFormat = new Format\AndroidSafetyNet($enc, $this->_authenticatorData); break;
case 'apple': $this->_attestationFormat = new Format\Apple($enc, $this->_authenticatorData); break;
@@ -47,6 +50,14 @@ class AttestationObject {
}
}
/**
* returns the attestation format name
* @return string
*/
public function getAttestationFormatName() {
return $this->_attestationFormatName;
}
/**
* returns the attestation public key in PEM format
* @return AuthenticatorData
@@ -72,16 +83,19 @@ class AttestationObject {
$issuer = '';
if ($pem) {
$certInfo = \openssl_x509_parse($pem);
if (\is_array($certInfo) && \is_array($certInfo['issuer'])) {
if ($certInfo['issuer']['CN']) {
$issuer .= \trim($certInfo['issuer']['CN']);
if (\is_array($certInfo) && \array_key_exists('issuer', $certInfo) && \is_array($certInfo['issuer'])) {
$cn = $certInfo['issuer']['CN'] ?? '';
$o = $certInfo['issuer']['O'] ?? '';
$ou = $certInfo['issuer']['OU'] ?? '';
if ($cn) {
$issuer .= $cn;
}
if ($certInfo['issuer']['O'] || $certInfo['issuer']['OU']) {
if ($issuer) {
$issuer .= ' (' . \trim($certInfo['issuer']['O'] . ' ' . $certInfo['issuer']['OU']) . ')';
} else {
$issuer .= \trim($certInfo['issuer']['O'] . ' ' . $certInfo['issuer']['OU']);
}
if ($issuer && ($o || $ou)) {
$issuer .= ' (' . trim($o . ' ' . $ou) . ')';
} else {
$issuer .= trim($o . ' ' . $ou);
}
}
}
@@ -98,16 +112,19 @@ class AttestationObject {
$subject = '';
if ($pem) {
$certInfo = \openssl_x509_parse($pem);
if (\is_array($certInfo) && \is_array($certInfo['subject'])) {
if ($certInfo['subject']['CN']) {
$subject .= \trim($certInfo['subject']['CN']);
if (\is_array($certInfo) && \array_key_exists('subject', $certInfo) && \is_array($certInfo['subject'])) {
$cn = $certInfo['subject']['CN'] ?? '';
$o = $certInfo['subject']['O'] ?? '';
$ou = $certInfo['subject']['OU'] ?? '';
if ($cn) {
$subject .= $cn;
}
if ($certInfo['subject']['O'] || $certInfo['subject']['OU']) {
if ($subject) {
$subject .= ' (' . \trim($certInfo['subject']['O'] . ' ' . $certInfo['subject']['OU']) . ')';
} else {
$subject .= \trim($certInfo['subject']['O'] . ' ' . $certInfo['subject']['OU']);
}
if ($subject && ($o || $ou)) {
$subject .= ' (' . trim($o . ' ' . $ou) . ')';
} else {
$subject .= trim($o . ' ' . $ou);
}
}
}

View File

@@ -1,9 +1,9 @@
<?php
namespace WebAuthn\Attestation;
use WebAuthn\WebAuthnException;
use WebAuthn\CBOR\CborDecoder;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn\Attestation;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\CBOR\CborDecoder;
use lbuchs\WebAuthn\Binary\ByteBuffer;
/**
* @author Lukas Buchs

View File

@@ -1,15 +1,16 @@
<?php
namespace WebAuthn\Attestation\Format;
use WebAuthn\WebAuthnException;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn\Attestation\Format;
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\Binary\ByteBuffer;
class AndroidKey extends FormatBase {
private $_alg;
private $_signature;
private $_x5c;
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
parent::__construct($AttestionObject, $authenticatorData);
// check u2f data

View File

@@ -1,9 +1,10 @@
<?php
namespace WebAuthn\Attestation\Format;
use WebAuthn\WebAuthnException;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn\Attestation\Format;
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\Binary\ByteBuffer;
class AndroidSafetyNet extends FormatBase {
private $_signature;
@@ -11,7 +12,7 @@ class AndroidSafetyNet extends FormatBase {
private $_x5c;
private $_payload;
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
parent::__construct($AttestionObject, $authenticatorData);
// check data

View File

@@ -1,14 +1,15 @@
<?php
namespace WebAuthn\Attestation\Format;
use WebAuthn\WebAuthnException;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn\Attestation\Format;
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\Binary\ByteBuffer;
class Apple extends FormatBase {
private $_x5c;
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
parent::__construct($AttestionObject, $authenticatorData);
// check packed data

View File

@@ -1,8 +1,9 @@
<?php
namespace WebAuthn\Attestation\Format;
use WebAuthn\WebAuthnException;
namespace lbuchs\WebAuthn\Attestation\Format;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
abstract class FormatBase {
@@ -14,9 +15,9 @@ abstract class FormatBase {
/**
*
* @param Array $AttestionObject
* @param \WebAuthn\Attestation\AuthenticatorData $authenticatorData
* @param AuthenticatorData $authenticatorData
*/
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
$this->_attestationObject = $AttestionObject;
$this->_authenticatorData = $authenticatorData;
}
@@ -26,7 +27,7 @@ abstract class FormatBase {
*/
public function __destruct() {
// delete X.509 chain certificate file after use
if (\is_file($this->_x5c_tempFile)) {
if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) {
\unlink($this->_x5c_tempFile);
}
}
@@ -36,7 +37,7 @@ abstract class FormatBase {
* @return string|null
*/
public function getCertificateChain() {
if (\is_file($this->_x5c_tempFile)) {
if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) {
return \file_get_contents($this->_x5c_tempFile);
}
return null;

View File

@@ -1,13 +1,14 @@
<?php
namespace WebAuthn\Attestation\Format;
use WebAuthn\WebAuthnException;
namespace lbuchs\WebAuthn\Attestation\Format;
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
use lbuchs\WebAuthn\WebAuthnException;
class None extends FormatBase {
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
parent::__construct($AttestionObject, $authenticatorData);
}
@@ -28,12 +29,13 @@ class None extends FormatBase {
}
/**
* validates the certificate against root certificates
* validates the certificate against root certificates.
* Format 'none' does not contain any ca, so always false.
* @param array $rootCas
* @return boolean
* @throws WebAuthnException
*/
public function validateRootCertificate($rootCas) {
return true;
return false;
}
}

View File

@@ -1,16 +1,17 @@
<?php
namespace WebAuthn\Attestation\Format;
use WebAuthn\WebAuthnException;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn\Attestation\Format;
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\Binary\ByteBuffer;
class Packed extends FormatBase {
private $_alg;
private $_signature;
private $_x5c;
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
parent::__construct($AttestionObject, $authenticatorData);
// check packed data

View File

@@ -1,9 +1,10 @@
<?php
namespace WebAuthn\Attestation\Format;
use WebAuthn\WebAuthnException;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn\Attestation\Format;
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\Binary\ByteBuffer;
class Tpm extends FormatBase {
private $_TPM_GENERATED_VALUE = "\xFF\x54\x43\x47";
@@ -19,7 +20,7 @@ class Tpm extends FormatBase {
private $_certInfo;
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
parent::__construct($AttestionObject, $authenticatorData);
// check packed data

View File

@@ -1,9 +1,10 @@
<?php
namespace WebAuthn\Attestation\Format;
use WebAuthn\WebAuthnException;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn\Attestation\Format;
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\Binary\ByteBuffer;
class U2f extends FormatBase {
private $_alg = -7;

View File

@@ -1,8 +1,8 @@
<?php
namespace WebAuthn\Binary;
use WebAuthn\WebAuthnException;
namespace lbuchs\WebAuthn\Binary;
use lbuchs\WebAuthn\WebAuthnException;
/**
* Modified version of https://github.com/madwizard-thomas/webauthn-server/blob/master/src/Format/ByteBuffer.php
@@ -39,7 +39,7 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
/**
* create a ByteBuffer from a base64 url encoded string
* @param string $base64url
* @return \WebAuthn\Binary\ByteBuffer
* @return ByteBuffer
*/
public static function fromBase64Url($base64url) {
$bin = self::_base64url_decode($base64url);
@@ -52,7 +52,7 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
/**
* create a ByteBuffer from a base64 url encoded string
* @param string $hex
* @return \WebAuthn\Binary\ByteBuffer
* @return ByteBuffer
*/
public static function fromHex($hex) {
$bin = \hex2bin($hex);
@@ -65,7 +65,7 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
/**
* create a random ByteBuffer
* @param string $length
* @return \WebAuthn\Binary\ByteBuffer
* @return ByteBuffer
*/
public static function randomBuffer($length) {
if (\function_exists('random_bytes')) { // >PHP 7.0
@@ -97,6 +97,14 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
return \ord(\substr($this->_data, $offset, 1));
}
public function getJson($jsonFlags=0) {
$data = \json_decode($this->getBinaryString(), null, 512, $jsonFlags);
if (\json_last_error() !== JSON_ERROR_NONE) {
throw new WebAuthnException(\json_last_error_msg(), WebAuthnException::BYTEBUFFER);
}
return $data;
}
public function getLength() {
return $this->_length;
}
@@ -203,7 +211,7 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
/**
* jsonSerialize interface
* return binary data in RFC 1342-Like serialized string
* @return \stdClass
* @return string
*/
public function jsonSerialize() {
if (ByteBuffer::$useBase64UrlEncoding) {
@@ -231,6 +239,36 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
$this->_length = \strlen($this->_data);
}
/**
* (PHP 8 deprecates Serializable-Interface)
* @return array
*/
public function __serialize() {
return [
'data' => \serialize($this->_data)
];
}
/**
* object to string
* @return string
*/
public function __toString() {
return $this->getHex();
}
/**
* (PHP 8 deprecates Serializable-Interface)
* @param array $data
* @return void
*/
public function __unserialize($data) {
if ($data && isset($data['data'])) {
$this->_data = \unserialize($data['data']);
$this->_length = \strlen($this->_data);
}
}
// -----------------------
// PROTECTED STATIC
// -----------------------

View File

@@ -1,9 +1,9 @@
<?php
namespace WebAuthn\CBOR;
use WebAuthn\WebAuthnException;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn\CBOR;
use lbuchs\WebAuthn\WebAuthnException;
use lbuchs\WebAuthn\Binary\ByteBuffer;
/**
* Modified version of https://github.com/madwizard-thomas/webauthn-server/blob/master/src/Format/CborDecoder.php

View File

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

View File

@@ -1,7 +1,7 @@
<?php
namespace WebAuthn;
use WebAuthn\Binary\ByteBuffer;
namespace lbuchs\WebAuthn;
use lbuchs\WebAuthn\Binary\ByteBuffer;
require_once 'WebAuthnException.php';
require_once 'Binary/ByteBuffer.php';
require_once 'Attestation/AttestationObject.php';
@@ -69,16 +69,20 @@ class WebAuthn {
/**
* add a root certificate to verify new registrations
* @param string $path file path of / directory with root certificates
* @param array|null $certFileExtensions if adding a direction, all files with provided extension are added. default: pem, crt, cer, der
*/
public function addRootCertificates($path) {
public function addRootCertificates($path, $certFileExtensions=null) {
if (!\is_array($this->_caFiles)) {
$this->_caFiles = array();
}
if ($certFileExtensions === null) {
$certFileExtensions = array('pem', 'crt', 'cer', 'der');
}
$path = \rtrim(\trim($path), '\\/');
if (\is_dir($path)) {
foreach (\scandir($path) as $ca) {
if (\is_file($path . '/' . $ca)) {
$this->addRootCertificates($path . '/' . $ca);
if (\is_file($path . DIRECTORY_SEPARATOR . $ca) && \in_array(\strtolower(\pathinfo($ca, PATHINFO_EXTENSION)), $certFileExtensions)) {
$this->addRootCertificates($path . DIRECTORY_SEPARATOR . $ca);
}
}
} else if (\is_file($path) && !\in_array(\realpath($path), $this->_caFiles)) {
@@ -273,10 +277,11 @@ class WebAuthn {
* @param string|ByteBuffer $challenge binary used challange
* @param bool $requireUserVerification true, if the device must verify user (e.g. by biometric data or pin)
* @param bool $requireUserPresent false, if the device must NOT check user presence (e.g. by pressing a button)
* @param bool $failIfRootMismatch false, if there should be no error thrown if root certificate doesn't match
* @return \stdClass
* @throws WebAuthnException
*/
public function processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true) {
public function processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true, $failIfRootMismatch=true) {
$clientDataHash = \hash('sha256', $clientDataJSON, true);
$clientData = \json_decode($clientDataJSON);
$challenge = $challenge instanceof ByteBuffer ? $challenge : new ByteBuffer($challenge);
@@ -318,18 +323,21 @@ class WebAuthn {
}
// 15. If validation is successful, obtain a list of acceptable trust anchors
if (is_array($this->_caFiles) && !$attestationObject->validateRootCertificate($this->_caFiles)) {
$rootValid = is_array($this->_caFiles) ? $attestationObject->validateRootCertificate($this->_caFiles) : null;
if ($failIfRootMismatch && is_array($this->_caFiles) && !$rootValid) {
throw new WebAuthnException('invalid root certificate', WebAuthnException::CERTIFICATE_NOT_TRUSTED);
}
// 10. Verify that the User Present bit of the flags in authData is set.
if ($requireUserPresent && !$attestationObject->getAuthenticatorData()->getUserPresent()) {
$userPresent = $attestationObject->getAuthenticatorData()->getUserPresent();
if ($requireUserPresent && !$userPresent) {
throw new WebAuthnException('user not present during authentication', WebAuthnException::USER_PRESENT);
}
// 11. If user verification is required for this registration, verify that the User Verified bit of the flags in authData is set.
if ($requireUserVerification && !$attestationObject->getAuthenticatorData()->getUserVerified()) {
throw new WebAuthnException('user not verificated during authentication', WebAuthnException::USER_VERIFICATED);
$userVerified = $attestationObject->getAuthenticatorData()->getUserVerified();
if ($requireUserVerification && !$userVerified) {
throw new WebAuthnException('user not verified during authentication', WebAuthnException::USER_VERIFICATED);
}
$signCount = $attestationObject->getAuthenticatorData()->getSignCount();
@@ -340,6 +348,7 @@ class WebAuthn {
// prepare data to store for future logins
$data = new \stdClass();
$data->rpId = $this->_rpId;
$data->attestationFormat = $attestationObject->getAttestationFormatName();
$data->credentialId = $attestationObject->getAuthenticatorData()->getCredentialId();
$data->credentialPublicKey = $attestationObject->getAuthenticatorData()->getPublicKeyPem();
$data->certificateChain = $attestationObject->getCertificateChain();
@@ -348,6 +357,9 @@ class WebAuthn {
$data->certificateSubject = $attestationObject->getCertificateSubject();
$data->signatureCounter = $this->_signatureCounter;
$data->AAGUID = $attestationObject->getAuthenticatorData()->getAAGUID();
$data->rootValid = $rootValid;
$data->userPresent = $userPresent;
$data->userVerified = $userVerified;
return $data;
}
@@ -453,6 +465,92 @@ class WebAuthn {
return true;
}
/**
* Downloads root certificates from FIDO Alliance Metadata Service (MDS) to a specific folder
* https://fidoalliance.org/metadata/
* @param string $certFolder Folder path to save the certificates in PEM format.
* @param bool $deleteCerts=true
* @return int number of cetificates
* @throws WebAuthnException
*/
public function queryFidoMetaDataService($certFolder, $deleteCerts=true) {
$url = 'https://mds.fidoalliance.org/';
$raw = null;
if (\function_exists('curl_init')) {
$ch = \curl_init($url);
\curl_setopt($ch, CURLOPT_HEADER, false);
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
\curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
\curl_setopt($ch, CURLOPT_USERAGENT, 'github.com/lbuchs/WebAuthn - A simple PHP WebAuthn server library');
$raw = \curl_exec($ch);
\curl_close($ch);
} else {
$raw = \file_get_contents($url);
}
$certFolder = \rtrim(\realpath($certFolder), '\\/');
if (!is_dir($certFolder)) {
throw new WebAuthnException('Invalid folder path for query FIDO Alliance Metadata Service');
}
if (!\is_string($raw)) {
throw new WebAuthnException('Unable to query FIDO Alliance Metadata Service');
}
$jwt = \explode('.', $raw);
if (\count($jwt) !== 3) {
throw new WebAuthnException('Invalid JWT from FIDO Alliance Metadata Service');
}
if ($deleteCerts) {
foreach (\scandir($certFolder) as $ca) {
if (\substr($ca, -4) === '.pem') {
if (\unlink($certFolder . DIRECTORY_SEPARATOR . $ca) === false) {
throw new WebAuthnException('Cannot delete certs in folder for FIDO Alliance Metadata Service');
}
}
}
}
list($header, $payload, $hash) = $jwt;
$payload = Binary\ByteBuffer::fromBase64Url($payload)->getJson();
$count = 0;
if (\is_object($payload) && \property_exists($payload, 'entries') && \is_array($payload->entries)) {
foreach ($payload->entries as $entry) {
if (\is_object($entry) && \property_exists($entry, 'metadataStatement') && \is_object($entry->metadataStatement)) {
$description = $entry->metadataStatement->description ?? null;
$attestationRootCertificates = $entry->metadataStatement->attestationRootCertificates ?? null;
if ($description && $attestationRootCertificates) {
// create filename
$certFilename = \preg_replace('/[^a-z0-9]/i', '_', $description);
$certFilename = \trim(\preg_replace('/\_{2,}/i', '_', $certFilename),'_') . '.pem';
$certFilename = \strtolower($certFilename);
// add certificate
$certContent = $description . "\n";
$certContent .= \str_repeat('-', \mb_strlen($description)) . "\n";
foreach ($attestationRootCertificates as $attestationRootCertificate) {
$count++;
$certContent .= "\n-----BEGIN CERTIFICATE-----\n";
$certContent .= \chunk_split(\trim($attestationRootCertificate), 64, "\n");
$certContent .= "-----END CERTIFICATE-----\n";
}
if (\file_put_contents($certFolder . DIRECTORY_SEPARATOR . $certFilename, $certContent) === false) {
throw new WebAuthnException('unable to save certificate from FIDO Alliance Metadata Service');
}
}
}
}
}
return $count;
}
// -----------------------------------------------
// PRIVATE
// -----------------------------------------------

View File

@@ -1,5 +1,5 @@
<?php
namespace WebAuthn;
namespace lbuchs\WebAuthn;
/**
* @author Lukas Buchs