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 / android-key.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
android-key.php
158 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\Methods\Passkeys\Byte_Buffer;
15 use WP2FA\Methods\Passkeys\Web_Authn_Exception;
16 use WP2FA\Admin\Methods\passkeys\Authenticator_Data;
17
18 /**
19 * Responsible for android format
20 *
21 * @since 3.0.0
22 */
23 class Android_Key extends Format_Base {
24
25 /**
26 * Algorithm to be used
27 *
28 * @var string
29 *
30 * @since 3.0.0
31 */
32 private $_alg;
33
34 /**
35 * Signature
36 *
37 * @var string
38 *
39 * @since 3.0.0
40 */
41 private $_signature;
42
43 /**
44 * X5c certificate
45 *
46 * @var string
47 *
48 * @since 3.0.0
49 */
50 private $_x5c;
51
52 /**
53 * Default constructor
54 *
55 * @param \StdClass $attestion_object - attestation object.
56 * @param \WP2FA\Admin\Methods\passkeys\Authenticator_Data $authenticator_data - Authentication data.
57 *
58 * @throws Web_Authn_Exception - if can not be created.
59 *
60 * @since 3.0.0
61 */
62 public function __construct( $attestion_object, Authenticator_Data $authenticator_data ) {
63 parent::__construct( $attestion_object, $authenticator_data );
64
65 // check u2f data.
66 $att_stmt = $this->attestation_object['att_stmt'];
67
68 if ( ! \array_key_exists( 'alg', $att_stmt ) || $this->_get_cose_algorithm( $att_stmt['alg'] ) === null ) {
69 throw new Web_Authn_Exception( 'unsupported alg: ' . $att_stmt['alg'], Web_Authn_Exception::INVALID_DATA );
70 }
71
72 if ( ! \array_key_exists( 'sig', $att_stmt ) || ! \is_object( $att_stmt['sig'] ) || ! ( $att_stmt['sig'] instanceof Byte_Buffer ) ) {
73 throw new Web_Authn_Exception( 'no signature found', Web_Authn_Exception::INVALID_DATA );
74 }
75
76 if ( ! \array_key_exists( 'x5c', $att_stmt ) || ! \is_array( $att_stmt['x5c'] ) || \count( $att_stmt['x5c'] ) < 1 ) {
77 throw new Web_Authn_Exception( 'invalid x5c certificate', Web_Authn_Exception::INVALID_DATA );
78 }
79
80 if ( ! \is_object( $att_stmt['x5c'][0] ) || ! ( $att_stmt['x5c'][0] instanceof Byte_Buffer ) ) {
81 throw new Web_Authn_Exception( 'invalid x5c certificate', Web_Authn_Exception::INVALID_DATA );
82 }
83
84 $this->_alg = $att_stmt['alg'];
85 $this->_signature = $att_stmt['sig']->getBinaryString();
86 $this->_x5c = $att_stmt['x5c'][0]->getBinaryString();
87
88 if ( count( $att_stmt['x5c'] ) > 1 ) {
89 for ( $i = 1; $i < count( $att_stmt['x5c'] ); $i++ ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
90 $this->x5c_chain[] = $att_stmt['x5c'][ $i ]->getBinaryString();
91 }
92 unset( $i );
93 }
94 }
95
96 /**
97 * Returns the key certificate in PEM format
98 *
99 * @return string
100 *
101 * @since 3.0.0
102 */
103 public function get_certificate_pem() {
104 return $this->_create_certificate_pem( $this->_x5c );
105 }
106
107 /**
108 * Validator
109 *
110 * @param string $client_data_hash - Hash collected.
111 *
112 * @throws Web_Authn_Exception - Throws exception if validation fails.
113 *
114 * @since 3.0.0
115 */
116 public function validate_attestation( $client_data_hash ) {
117 $public_key = \openssl_pkey_get_public( $this->get_certificate_pem() );
118
119 if ( false === $public_key ) {
120 throw new Web_Authn_Exception( 'invalid public key: ' . \openssl_error_string(), Web_Authn_Exception::INVALID_PUBLIC_KEY );
121 }
122
123 // Verify that sig is a valid signature over the concatenation of authenticator_data and client_data_hash
124 // using the attestation public key in attestn_cert with the algorithm specified in alg.
125 $data_to_verify = $this->authenticator_data->get_binary();
126 $data_to_verify .= $client_data_hash;
127
128 $cose_algorithm = $this->_get_cose_algorithm( $this->_alg );
129
130 // check certificate.
131 return \openssl_verify( $data_to_verify, $this->_signature, $public_key, $cose_algorithm->openssl ) === 1;
132 }
133
134 /**
135 * Validates the certificate against root certificates
136 *
137 * @param array $root_cas - Array with values.
138 *
139 * @return boolean
140 *
141 * @throws Web_Authn_Exception - Throws exception.
142 *
143 * @since 3.0.0
144 */
145 public function validateRootCertificate( $root_cas ) {
146 $chain_c = $this->_create_x5c_chain_file();
147 if ( $chain_c ) {
148 $root_cas[] = $chain_c;
149 }
150
151 $v = \openssl_x509_checkpurpose( $this->get_certificate_pem(), -1, $root_cas );
152 if ( -1 === $v ) {
153 throw new Web_Authn_Exception( 'error on validating root certificate: ' . \openssl_error_string(), Web_Authn_Exception::CERTIFICATE_NOT_TRUSTED );
154 }
155 return $v;
156 }
157 }
158