PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / Methods / passkeys / format / format-base.php
wp-2fa / includes / classes / Admin / Methods / passkeys / format Last commit date
android-key.php 1 day ago android-safety-net.php 1 day ago apple.php 1 day ago format-base.php 1 day ago index.php 1 day ago none.php 1 day ago packed.php 1 day ago tpm.php 1 day ago u2f.php 1 day ago
format-base.php
272 lines
1 <?php
2 /**
3 * Passkeys formatters
4 *
5 * @package wp-2fa
6 * @since 3.0.0
7 * @copyright 2026 Melapress
8 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
9 * @link https://wordpress.org/plugins/wp-2fa/
10 */
11
12 namespace WP2FA\Passkeys\Format;
13
14 use WP2FA\Admin\Methods\passkeys\Authenticator_Data;
15
16 /**
17 * Base format class
18 *
19 * @since 3.0.0
20 */
21 abstract class Format_Base {
22
23 /**
24 * Attestation object
25 *
26 * @var Array
27 *
28 * @since 3.1.0
29 */
30 protected $attestation_object = null;
31
32 /**
33 * Authenticator data
34 *
35 * @var Authenticator_Data
36 *
37 * @since 3.1.0
38 */
39 protected $authenticator_data = null;
40
41 /**
42 * X5c certificate chain
43 *
44 * @var array
45 *
46 * @since 3.0.0
47 */
48 protected $x5c_chain = array();
49
50 /**
51 * Temporary file for certificate chain
52 *
53 * @var string
54 *
55 * @since 3.0.0
56 */
57 protected $x5c_temp_file = null;
58
59 /**
60 * Default constructor
61 *
62 * @param Array $attestation_object - Default comment.
63 * @param Authenticator_Data $authenticator_data - Default comment.
64 *
65 * @since 3.1.0
66 */
67 public function __construct( $attestation_object, Authenticator_Data $authenticator_data ) {
68 $this->attestation_object = $attestation_object;
69 $this->authenticator_data = $authenticator_data;
70 }
71
72 /**
73 * Default destructor
74 *
75 * @since 3.0.0
76 */
77 public function __destruct() {
78 // delete X.509 chain certificate file after use.
79 if ( $this->x5c_temp_file && \is_file( $this->x5c_temp_file ) ) {
80 \wp_delete_file( $this->x5c_temp_file );
81 }
82 }
83
84 /**
85 * Returns the certificate chain in PEM format.
86 *
87 * @return string|null
88 *
89 * @since 3.0.0
90 */
91 public function get_certificate_chain() {
92 if ( $this->x5c_temp_file && \is_file( $this->x5c_temp_file ) ) {
93 return \file_get_contents( $this->x5c_temp_file );
94 }
95 return null;
96 }
97
98 /**
99 * Returns the key X.509 certificate in PEM format
100 *
101 * @return string
102 *
103 * @since 3.0.0
104 */
105 public function get_certificate_pem() {
106 // need to be overwritten.
107 return null;
108 }
109
110 /**
111 * Checks validity of the signature
112 *
113 * @param string $client_data_hash - Default comment.
114 *
115 * @return bool
116 *
117 * @throws Web_Authn_Exception - Default comment.
118 *
119 * @since 3.0.0
120 */
121 public function validate_attestation( $client_data_hash ) {
122 // need to be overwritten.
123 return false;
124 }
125
126 /**
127 * Validates the certificate against root certificates
128 *
129 * @param array $root_cas - Default comment.
130 *
131 * @return boolean
132 *
133 * @throws Web_Authn_Exception - Default comment.
134 *
135 * @since 3.0.0
136 */
137 public function validate_root_certificate( $root_cas ) {
138 // need to be overwritten.
139 return false;
140 }
141
142 /**
143 * Create a PEM encoded certificate with X.509 binary data
144 *
145 * @param string $x5c - Default comment.
146 *
147 * @return string
148 *
149 * @since 3.0.0
150 */
151 protected function _create_certificate_pem( $x5c ) {
152 $pem = '-----BEGIN CERTIFICATE-----' . "\n";
153 $pem .= \chunk_split( \base64_encode( $x5c ), 64, "\n" );
154 $pem .= '-----END CERTIFICATE-----' . "\n";
155 return $pem;
156 }
157
158 /**
159 * Creates a PEM encoded chain file
160 *
161 * @return string|null
162 *
163 * @since 3.0.0
164 */
165 protected function _create_x5c_chain_file() {
166 $content = '';
167 if ( \is_array( $this->x5c_chain ) && \count( $this->x5c_chain ) > 0 ) {
168 foreach ( $this->x5c_chain as $x5c ) {
169 $cert_info = \openssl_x509_parse( $this->_create_certificate_pem( $x5c ) );
170
171 // check if certificate is self signed.
172 if ( \is_array( $cert_info ) && \is_array( $cert_info['issuer'] ) && \is_array( $cert_info['subject'] ) ) {
173 $self_signed = false;
174
175 $subject_key_identifier = $cert_info['extensions']['subjectKeyIdentifier'] ?? null;
176 $authority_key_identifier = $cert_info['extensions']['authorityKeyIdentifier'] ?? null;
177
178 if ( $authority_key_identifier && substr( $authority_key_identifier, 0, 6 ) === 'keyid:' ) {
179 $authority_key_identifier = substr( $authority_key_identifier, 6 );
180 }
181 if ( $subject_key_identifier && substr( $subject_key_identifier, 0, 6 ) === 'keyid:' ) {
182 $subject_key_identifier = substr( $subject_key_identifier, 6 );
183 }
184
185 if ( ( $subject_key_identifier && ! $authority_key_identifier ) || ( $authority_key_identifier && $authority_key_identifier === $subject_key_identifier ) ) {
186 $self_signed = true;
187 }
188
189 if ( ! $self_signed ) {
190 $content .= "\n" . $this->_create_certificate_pem( $x5c ) . "\n";
191 }
192 }
193 }
194 }
195
196 if ( $content ) {
197 $this->x5c_temp_file = \tempnam( \sys_get_temp_dir(), 'x5c_' );
198 if ( \file_put_contents( $this->x5c_temp_file, $content ) !== false ) {
199 return $this->x5c_temp_file;
200 }
201 }
202
203 return null;
204 }
205
206 /**
207 * Returns the name and openssl key for provided cose number.
208 *
209 * @param int $cose_number - Default comment.
210 *
211 * @return \stdClass|null
212 *
213 * @since 3.0.0
214 */
215 protected function _get_cose_algorithm( $cose_number ) {
216 // https://www.iana.org/assignments/cose/cose.xhtml#algorithms .
217 $cose_algorithms = array(
218 array(
219 'hash' => 'SHA1',
220 'openssl' => OPENSSL_ALGO_SHA1,
221 'cose' => array(
222 -65535, // RS1.
223 ),
224 ),
225
226 array(
227 'hash' => 'SHA256',
228 'openssl' => OPENSSL_ALGO_SHA256,
229 'cose' => array(
230 -257, // RS256.
231 -37, // PS256.
232 -7, // ES256.
233 5, // HMAC256.
234 ),
235 ),
236
237 array(
238 'hash' => 'SHA384',
239 'openssl' => OPENSSL_ALGO_SHA384,
240 'cose' => array(
241 -258, // RS384.
242 -38, // PS384.
243 -35, // ES384.
244 6, // HMAC384.
245 ),
246 ),
247
248 array(
249 'hash' => 'SHA512',
250 'openssl' => OPENSSL_ALGO_SHA512,
251 'cose' => array(
252 -259, // RS512.
253 -39, // PS512.
254 -36, // ES512.
255 7, // HMAC512.
256 ),
257 ),
258 );
259
260 foreach ( $cose_algorithms as $cose_algorithm ) {
261 if ( \in_array( $cose_number, $cose_algorithm['cose'], true ) ) {
262 $return = new \stdClass();
263 $return->hash = $cose_algorithm['hash'];
264 $return->openssl = $cose_algorithm['openssl'];
265 return $return;
266 }
267 }
268
269 return null;
270 }
271 }
272