1 <?php 2 3 4 namespace lbuchs\WebAuthn\Attestation\Format; 5 use lbuchs\WebAuthn\WebAuthnException; 6 use lbuchs\WebAuthn\Attestation\AuthenticatorData; 7 8 9 abstract class FormatBase { 10 protected $_attestationObject = null; 11 protected $_authenticatorData = null; 12 protected $_x5c_chain = array(); 13 protected $_x5c_tempFile = null; 14 15 /** 16 * 17 * @param Array $AttestionObject 18 * @param AuthenticatorData $authenticatorData 19 */ 20 public function __construct($AttestionObject, AuthenticatorData $authenticatorData) { 21 $this->_attestationObject = $AttestionObject; 22 $this->_authenticatorData = $authenticatorData; 23 } 24 25 /** 26 * 27 */ 28 public function __destruct() { 29 // delete X.509 chain certificate file after use 30 if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) { 31 \unlink($this->_x5c_tempFile); 32 } 33 } 34 35 /** 36 * returns the certificate chain in PEM format 37 * @return string|null 38 */ 39 public function getCertificateChain() { 40 if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) { 41 return \file_get_contents($this->_x5c_tempFile); 42 } 43 return null; 44 } 45 46 /** 47 * returns the key X.509 certificate in PEM format 48 * @return string 49 */ 50 public function getCertificatePem() { 51 // need to be overwritten 52 return null; 53 } 54 55 /** 56 * checks validity of the signature 57 * @param string $clientDataHash 58 * @return bool 59 * @throws WebAuthnException 60 */ 61 public function validateAttestation($clientDataHash) { 62 // need to be overwritten 63 return false; 64 } 65 66 /** 67 * validates the certificate against root certificates 68 * @param array $rootCas 69 * @return boolean 70 * @throws WebAuthnException 71 */ 72 public function validateRootCertificate($rootCas) { 73 // need to be overwritten 74 return false; 75 } 76 77 78 /** 79 * create a PEM encoded certificate with X.509 binary data 80 * @param string $x5c 81 * @return string 82 */ 83 protected function _createCertificatePem($x5c) { 84 $pem = '-----BEGIN CERTIFICATE-----' . "\n"; 85 $pem .= \chunk_split(\base64_encode($x5c), 64, "\n"); 86 $pem .= '-----END CERTIFICATE-----' . "\n"; 87 return $pem; 88 } 89 90 /** 91 * creates a PEM encoded chain file 92 * @return type 93 */ 94 protected function _createX5cChainFile() { 95 $content = ''; 96 if (\is_array($this->_x5c_chain) && \count($this->_x5c_chain) > 0) { 97 foreach ($this->_x5c_chain as $x5c) { 98 $certInfo = \openssl_x509_parse($this->_createCertificatePem($x5c)); 99 100 // check if certificate is self signed 101 if (\is_array($certInfo) && \is_array($certInfo['issuer']) && \is_array($certInfo['subject'])) { 102 $selfSigned = false; 103 104 $subjectKeyIdentifier = $certInfo['extensions']['subjectKeyIdentifier'] ?? null; 105 $authorityKeyIdentifier = $certInfo['extensions']['authorityKeyIdentifier'] ?? null; 106 107 if ($authorityKeyIdentifier && substr($authorityKeyIdentifier, 0, 6) === 'keyid:') { 108 $authorityKeyIdentifier = substr($authorityKeyIdentifier, 6); 109 } 110 if ($subjectKeyIdentifier && substr($subjectKeyIdentifier, 0, 6) === 'keyid:') { 111 $subjectKeyIdentifier = substr($subjectKeyIdentifier, 6); 112 } 113 114 if (($subjectKeyIdentifier && !$authorityKeyIdentifier) || ($authorityKeyIdentifier && $authorityKeyIdentifier === $subjectKeyIdentifier)) { 115 $selfSigned = true; 116 } 117 118 if (!$selfSigned) { 119 $content .= "\n" . $this->_createCertificatePem($x5c) . "\n"; 120 } 121 } 122 } 123 } 124 125 if ($content) { 126 $this->_x5c_tempFile = \sys_get_temp_dir() . '/x5c_chain_' . \base_convert(\rand(), 10, 36) . '.pem'; 127 if (\file_put_contents($this->_x5c_tempFile, $content) !== false) { 128 return $this->_x5c_tempFile; 129 } 130 } 131 132 return null; 133 } 134 135 136 /** 137 * returns the name and openssl key for provided cose number. 138 * @param int $coseNumber 139 * @return \stdClass|null 140 */ 141 protected function _getCoseAlgorithm($coseNumber) { 142 // https://www.iana.org/assignments/cose/cose.xhtml#algorithms 143 $coseAlgorithms = array( 144 array( 145 'hash' => 'SHA1', 146 'openssl' => OPENSSL_ALGO_SHA1, 147 'cose' => array( 148 -65535 // RS1 149 )), 150 151 array( 152 'hash' => 'SHA256', 153 'openssl' => OPENSSL_ALGO_SHA256, 154 'cose' => array( 155 -257, // RS256 156 -37, // PS256 157 -7, // ES256 158 5 // HMAC256 159 )), 160 161 array( 162 'hash' => 'SHA384', 163 'openssl' => OPENSSL_ALGO_SHA384, 164 'cose' => array( 165 -258, // RS384 166 -38, // PS384 167 -35, // ES384 168 6 // HMAC384 169 )), 170 171 array( 172 'hash' => 'SHA512', 173 'openssl' => OPENSSL_ALGO_SHA512, 174 'cose' => array( 175 -259, // RS512 176 -39, // PS512 177 -36, // ES512 178 7 // HMAC512 179 )) 180 ); 181 182 foreach ($coseAlgorithms as $coseAlgorithm) { 183 if (\in_array($coseNumber, $coseAlgorithm['cose'], true)) { 184 $return = new \stdClass(); 185 $return->hash = $coseAlgorithm['hash']; 186 $return->openssl = $coseAlgorithm['openssl']; 187 return $return; 188 } 189 } 190 191 return null; 192 } 193 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body