| <?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 = \sys_get_temp_dir() . '/x5c_chain_' . \base_convert(\rand(), 10, 36) . '.pem'; |
| 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; |
| } |
| } |