194 lines
5.9 KiB
PHP
194 lines
5.9 KiB
PHP
<?php
|
|
|
|
|
|
namespace lbuchs\WebAuthn\Attestation\Format;
|
|
use lbuchs\WebAuthn\WebAuthnException;
|
|
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
|
|
|
|
|
|
abstract class FormatBase {
|
|
protected $_attestationObject = null;
|
|
protected $_authenticatorData = null;
|
|
protected $_x5c_chain = array();
|
|
protected $_x5c_tempFile = null;
|
|
|
|
/**
|
|
*
|
|
* @param Array $AttestionObject
|
|
* @param AuthenticatorData $authenticatorData
|
|
*/
|
|
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
|
|
$this->_attestationObject = $AttestionObject;
|
|
$this->_authenticatorData = $authenticatorData;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function __destruct() {
|
|
// delete X.509 chain certificate file after use
|
|
if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) {
|
|
\unlink($this->_x5c_tempFile);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* returns the certificate chain in PEM format
|
|
* @return string|null
|
|
*/
|
|
public function getCertificateChain() {
|
|
if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) {
|
|
return \file_get_contents($this->_x5c_tempFile);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* returns the key X.509 certificate in PEM format
|
|
* @return string
|
|
*/
|
|
public function getCertificatePem() {
|
|
// need to be overwritten
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* checks validity of the signature
|
|
* @param string $clientDataHash
|
|
* @return bool
|
|
* @throws WebAuthnException
|
|
*/
|
|
public function validateAttestation($clientDataHash) {
|
|
// need to be overwritten
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* validates the certificate against root certificates
|
|
* @param array $rootCas
|
|
* @return boolean
|
|
* @throws WebAuthnException
|
|
*/
|
|
public function validateRootCertificate($rootCas) {
|
|
// need to be overwritten
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* create a PEM encoded certificate with X.509 binary data
|
|
* @param string $x5c
|
|
* @return string
|
|
*/
|
|
protected function _createCertificatePem($x5c) {
|
|
$pem = '-----BEGIN CERTIFICATE-----' . "\n";
|
|
$pem .= \chunk_split(\base64_encode($x5c), 64, "\n");
|
|
$pem .= '-----END CERTIFICATE-----' . "\n";
|
|
return $pem;
|
|
}
|
|
|
|
/**
|
|
* creates a PEM encoded chain file
|
|
* @return type
|
|
*/
|
|
protected function _createX5cChainFile() {
|
|
$content = '';
|
|
if (\is_array($this->_x5c_chain) && \count($this->_x5c_chain) > 0) {
|
|
foreach ($this->_x5c_chain as $x5c) {
|
|
$certInfo = \openssl_x509_parse($this->_createCertificatePem($x5c));
|
|
|
|
// check if certificate is self signed
|
|
if (\is_array($certInfo) && \is_array($certInfo['issuer']) && \is_array($certInfo['subject'])) {
|
|
$selfSigned = false;
|
|
|
|
$subjectKeyIdentifier = $certInfo['extensions']['subjectKeyIdentifier'] ?? null;
|
|
$authorityKeyIdentifier = $certInfo['extensions']['authorityKeyIdentifier'] ?? null;
|
|
|
|
if ($authorityKeyIdentifier && substr($authorityKeyIdentifier, 0, 6) === 'keyid:') {
|
|
$authorityKeyIdentifier = substr($authorityKeyIdentifier, 6);
|
|
}
|
|
if ($subjectKeyIdentifier && substr($subjectKeyIdentifier, 0, 6) === 'keyid:') {
|
|
$subjectKeyIdentifier = substr($subjectKeyIdentifier, 6);
|
|
}
|
|
|
|
if (($subjectKeyIdentifier && !$authorityKeyIdentifier) || ($authorityKeyIdentifier && $authorityKeyIdentifier === $subjectKeyIdentifier)) {
|
|
$selfSigned = true;
|
|
}
|
|
|
|
if (!$selfSigned) {
|
|
$content .= "\n" . $this->_createCertificatePem($x5c) . "\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($content) {
|
|
$this->_x5c_tempFile = \tempnam(\sys_get_temp_dir(), 'x5c_');
|
|
if (\file_put_contents($this->_x5c_tempFile, $content) !== false) {
|
|
return $this->_x5c_tempFile;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* returns the name and openssl key for provided cose number.
|
|
* @param int $coseNumber
|
|
* @return \stdClass|null
|
|
*/
|
|
protected function _getCoseAlgorithm($coseNumber) {
|
|
// https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
|
$coseAlgorithms = array(
|
|
array(
|
|
'hash' => 'SHA1',
|
|
'openssl' => OPENSSL_ALGO_SHA1,
|
|
'cose' => array(
|
|
-65535 // RS1
|
|
)),
|
|
|
|
array(
|
|
'hash' => 'SHA256',
|
|
'openssl' => OPENSSL_ALGO_SHA256,
|
|
'cose' => array(
|
|
-257, // RS256
|
|
-37, // PS256
|
|
-7, // ES256
|
|
5 // HMAC256
|
|
)),
|
|
|
|
array(
|
|
'hash' => 'SHA384',
|
|
'openssl' => OPENSSL_ALGO_SHA384,
|
|
'cose' => array(
|
|
-258, // RS384
|
|
-38, // PS384
|
|
-35, // ES384
|
|
6 // HMAC384
|
|
)),
|
|
|
|
array(
|
|
'hash' => 'SHA512',
|
|
'openssl' => OPENSSL_ALGO_SHA512,
|
|
'cose' => array(
|
|
-259, // RS512
|
|
-39, // PS512
|
|
-36, // ES512
|
|
7 // HMAC512
|
|
))
|
|
);
|
|
|
|
foreach ($coseAlgorithms as $coseAlgorithm) {
|
|
if (\in_array($coseNumber, $coseAlgorithm['cose'], true)) {
|
|
$return = new \stdClass();
|
|
$return->hash = $coseAlgorithm['hash'];
|
|
$return->openssl = $coseAlgorithm['openssl'];
|
|
return $return;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|