_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; } }