FormatBase.php 5.3 KB

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