blob: b2f1dfd7d81538c180df40321e26e8647d8bbe98 [file] [log] [blame]
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001<?php
2
3
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01004namespace lbuchs\WebAuthn\Attestation\Format;
5use lbuchs\WebAuthn\WebAuthnException;
6use lbuchs\WebAuthn\Attestation\AuthenticatorData;
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01007
8
9abstract 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
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010018 * @param AuthenticatorData $authenticatorData
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010019 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010020 public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010021 $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
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010030 if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010031 \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() {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010040 if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) {
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010041 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 // check if issuer = subject (self signed)
100 if (\is_array($certInfo) && \is_array($certInfo['issuer']) && \is_array($certInfo['subject'])) {
101 $selfSigned = true;
102 foreach ($certInfo['issuer'] as $k => $v) {
103 if ($certInfo['subject'][$k] !== $v) {
104 $selfSigned = false;
105 break;
106 }
107 }
108
109 if (!$selfSigned) {
110 $content .= "\n" . $this->_createCertificatePem($x5c) . "\n";
111 }
112 }
113 }
114 }
115
116 if ($content) {
117 $this->_x5c_tempFile = \sys_get_temp_dir() . '/x5c_chain_' . \base_convert(\rand(), 10, 36) . '.pem';
118 if (\file_put_contents($this->_x5c_tempFile, $content) !== false) {
119 return $this->_x5c_tempFile;
120 }
121 }
122
123 return null;
124 }
125
126
127 /**
128 * returns the name and openssl key for provided cose number.
129 * @param int $coseNumber
130 * @return \stdClass|null
131 */
132 protected function _getCoseAlgorithm($coseNumber) {
133 // https://www.iana.org/assignments/cose/cose.xhtml#algorithms
134 $coseAlgorithms = array(
135 array(
136 'hash' => 'SHA1',
137 'openssl' => OPENSSL_ALGO_SHA1,
138 'cose' => array(
139 -65535 // RS1
140 )),
141
142 array(
143 'hash' => 'SHA256',
144 'openssl' => OPENSSL_ALGO_SHA256,
145 'cose' => array(
146 -257, // RS256
147 -37, // PS256
148 -7, // ES256
149 5 // HMAC256
150 )),
151
152 array(
153 'hash' => 'SHA384',
154 'openssl' => OPENSSL_ALGO_SHA384,
155 'cose' => array(
156 -258, // RS384
157 -38, // PS384
158 -35, // ES384
159 6 // HMAC384
160 )),
161
162 array(
163 'hash' => 'SHA512',
164 'openssl' => OPENSSL_ALGO_SHA512,
165 'cose' => array(
166 -259, // RS512
167 -39, // PS512
168 -36, // ES512
169 7 // HMAC512
170 ))
171 );
172
173 foreach ($coseAlgorithms as $coseAlgorithm) {
174 if (\in_array($coseNumber, $coseAlgorithm['cose'], true)) {
175 $return = new \stdClass();
176 $return->hash = $coseAlgorithm['hash'];
177 $return->openssl = $coseAlgorithm['openssl'];
178 return $return;
179 }
180 }
181
182 return null;
183 }
184}