@@ -15,69 +15,61 @@ class U2F
1515 /** @internal */
1616 const PUBKEY_LEN = 65 ;
1717
18- /** @var string */
19- private $ appId ;
20-
21- /** @var null|string */
22- private $ attestDir ;
23-
2418 /** @internal */
25- private $ FIXCERTS = array (
19+ private static $ FIXCERTS = [
2620 '349bca1031f8c82c4ceca38b9cebf1a69df9fb3b94eed99eb3fb9aa3822d26e8 ' ,
2721 'dd574527df608e47ae45fbba75a2afdd5c20fd94a02419381813cd55a2a3398f ' ,
2822 '1d8764f0f7cd1352df6150045c8f638e517270e8b5dda1c63ade9c2280240cae ' ,
2923 'd0edc9a91a1677435a953390865d208c55b3183c6759c9b5a7ff494c322558eb ' ,
3024 '6073c436dcd064a48127ddbf6032ac1a66fd59a0c24434f070d4e564c124c897 ' ,
3125 'ca993121846c464d666096d35f13bf44c1b05af205f9b4a1e00cf6cc10c5e511 '
32- ) ;
26+ ] ;
3327
3428 /**
35- * @param string $appId Application id for the running application
36- * @param string|null $attestDir Directory where trusted attestation roots may be found
3729 * @throws U2FException If OpenSSL older than 1.0.0 is used
3830 */
39- public function __construct ( $ appId , $ attestDir = null )
31+ public static function checkOpenSSLVersion ( )
4032 {
4133 if (OPENSSL_VERSION_NUMBER < 0x10000000 ) {
4234 throw new U2FException (
4335 'OpenSSL has to be at least version 1.0.0, this is ' . OPENSSL_VERSION_TEXT ,
4436 U2FException::OLD_OPENSSL
4537 );
4638 }
47- $ this ->appId = $ appId ;
48- $ this ->attestDir = $ attestDir ;
39+ return true ;
4940 }
5041
5142 /**
5243 * Called to get a registration request to send to a user.
5344 * Returns an array of one registration request and a array of sign requests.
5445 *
46+ * @param string $appId Application id for the running application, Basically the app's URL
5547 * @param array $registrations List of current registrations for this
5648 * user, to prevent the user from registering the same authenticator several
5749 * times.
5850 * @return array An array of two elements, the first containing a
5951 * RegisterRequest the second being an array of SignRequest
6052 * @throws U2FException
6153 */
62- public function makeRegistration (array $ registrations = array ())
54+ public static function makeRegistration ($ appId , array $ registrations = array ())
6355 {
64- $ challenge = $ this ->createChallenge ();
65- $ request = new RegistrationRequest ($ challenge , $ this ->appId );
66- $ signatures = $ this ->makeAuthentication ($ registrations );
67- return array ($ request , $ signatures );
56+ $ request = new RegistrationRequest (static ::createChallenge (), $ appId );
57+ $ signatures = static ::makeAuthentication ($ registrations , $ appId );
58+ return [$ request , $ signatures ];
6859 }
6960
7061 /**
7162 * Called to verify and unpack a registration message.
7263 *
7364 * @param RegistrationRequest $request this is a reply to
7465 * @param object $response response from a user
66+ * @param string $attestDir
7567 * @param bool $includeCert set to true if the attestation certificate should be
7668 * included in the returned Registration object
7769 * @return Registration
7870 * @throws U2FException
7971 */
80- public function register (RegistrationRequest $ request , $ response , $ includeCert = true )
72+ public static function register (RegistrationRequest $ request , $ response, $ attestDir = null , $ includeCert = true )
8173 {
8274 // Parameter Checks
8375 if ( !is_object ( $ request ) ) {
@@ -100,9 +92,9 @@ public function register(RegistrationRequest $request, $response, $includeCert =
10092 }
10193
10294 // Unpack the registration data coming from the client-side token
103- $ rawRegistration = $ this -> base64u_decode ($ response ->registrationData );
95+ $ rawRegistration = static :: base64u_decode ($ response ->registrationData );
10496 $ registrationData = array_values (unpack ('C* ' , $ rawRegistration ));
105- $ clientData = $ this -> base64u_decode ($ response ->clientData );
97+ $ clientData = static :: base64u_decode ($ response ->clientData );
10698 $ clientToken = json_decode ($ clientData );
10799
108100 // Check Client's challenge matches the original request's challenge
@@ -120,7 +112,7 @@ public function register(RegistrationRequest $request, $response, $includeCert =
120112 $ offset += U2F ::PUBKEY_LEN ;
121113
122114 // Validate and set the public key
123- if ($ this -> publicKeyToPem ($ pubKey ) === null ) {
115+ if (static :: publicKeyToPem ($ pubKey ) === null ) {
124116 throw new U2FException (
125117 'Decoding of public key failed ' ,
126118 U2FException::PUBKEY_DECODE
@@ -132,7 +124,7 @@ public function register(RegistrationRequest $request, $response, $includeCert =
132124 $ keyHandleLength = $ registrationData [$ offset ++];
133125 $ keyHandle = substr ($ rawRegistration , $ offset , $ keyHandleLength );
134126 $ offset += $ keyHandleLength ;
135- $ registration ->setKeyHandle ($ this -> base64u_encode ($ keyHandle ));
127+ $ registration ->setKeyHandle (static :: base64u_encode ($ keyHandle ));
136128
137129 // Build certificate
138130 // Set certificate length
@@ -142,7 +134,7 @@ public function register(RegistrationRequest $request, $response, $includeCert =
142134 $ certLength += $ registrationData [$ offset + 3 ];
143135
144136 // Write the certificate from the returning registration data
145- $ rawCert = $ this -> fixSignatureUnusedBits (substr ($ rawRegistration , $ offset , $ certLength ));
137+ $ rawCert = static :: fixSignatureUnusedBits (substr ($ rawRegistration , $ offset , $ certLength ));
146138 $ offset += $ certLength ;
147139 $ pemCert = "-----BEGIN CERTIFICATE----- \r\n" ;
148140 $ pemCert .= chunk_split (base64_encode ($ rawCert ), 64 );
@@ -152,8 +144,8 @@ public function register(RegistrationRequest $request, $response, $includeCert =
152144 }
153145
154146 // If we've set the attestDir, check the given certificate can be used.
155- if ($ this -> attestDir ) {
156- if (openssl_x509_checkpurpose ($ pemCert , -1 , $ this -> get_certs ()) !== true ) {
147+ if ($ attestDir ) {
148+ if (openssl_x509_checkpurpose ($ pemCert , -1 , static :: get_certs ($ attestDir )) !== true ) {
157149 throw new U2FException (
158150 'Attestation certificate can not be validated ' ,
159151 U2FException::ATTESTATION_VERIFICATION
@@ -194,21 +186,22 @@ public function register(RegistrationRequest $request, $response, $includeCert =
194186 * Called to get an authentication request.
195187 *
196188 * @param array $registrations An array of the registrations to create authentication requests for.
189+ * @param string $appId Application id for the running application, Basically the app's URL
197190 * @return array An array of SignRequest
198191 * @throws \InvalidArgumentException
199192 */
200- public function makeAuthentication (array $ registrations )
193+ public static function makeAuthentication (array $ registrations, $ appId )
201194 {
202195 $ signatures = [];
203196 foreach ($ registrations as $ reg ) {
204197 if ( !is_object ( $ reg ) ) {
205- throw new \InvalidArgumentException ('$registrations of getAuthenticateData () method only accepts array of object. ' );
198+ throw new \InvalidArgumentException ('$registrations of makeAuthentication () method only accepts array of object. ' );
206199 }
207200
208201 $ signatures [] = new SignRequest ([
209- 'appId ' => $ this -> appId ,
202+ 'appId ' => $ appId ,
210203 'keyHandle ' => $ reg ->keyHandle ,
211- 'challenge ' => $ this -> createChallenge (),
204+ 'challenge ' => static :: createChallenge (),
212205 ]);
213206 }
214207 return $ signatures ;
@@ -218,7 +211,7 @@ public function makeAuthentication(array $registrations)
218211 * Called to verify an authentication response
219212 *
220213 * @param array $requests An array of outstanding authentication requests
221- * @param array $registrations An array of current registrations
214+ * @param array <Registration> $registrations An array of current registrations
222215 * @param object $response A response from the authenticator
223216 * @return Registration
224217 * @throws U2FException
@@ -228,8 +221,9 @@ public function makeAuthentication(array $registrations)
228221 * If the Error returned is ERR_COUNTER_TOO_LOW this is an indication of
229222 * token cloning or similar and appropriate action should be taken.
230223 */
231- public function authenticate (array $ requests , array $ registrations , $ response )
224+ public static function authenticate (array $ requests , array $ registrations , $ response )
232225 {
226+ // Parameter checks
233227 if ( !is_object ( $ response ) ) {
234228 throw new \InvalidArgumentException ('$response of authenticate() method only accepts object. ' );
235229 }
@@ -241,17 +235,21 @@ public function authenticate(array $requests, array $registrations, $response)
241235 );
242236 }
243237
238+ // Set default values to null, so we get fails by default
244239 /** @var object|null $req */
245240 $ req = null ;
246241
247242 /** @var object|null $reg */
248243 $ reg = null ;
249244
250- $ clientData = $ this ->base64u_decode ($ response ->clientData );
245+ // Extract client response data
246+ $ clientData = static ::base64u_decode ($ response ->clientData );
251247 $ decodedClient = json_decode ($ clientData );
248+
249+ // Check we have a match among the requests and the response
252250 foreach ($ requests as $ req ) {
253251 if ( !is_object ( $ req ) ) {
254- throw new \InvalidArgumentException ('$requests of authenticate() method only accepts array of object . ' );
252+ throw new \InvalidArgumentException ('$requests of authenticate() method only accepts an array of objects . ' );
255253 }
256254
257255 if ($ req ->keyHandle === $ response ->keyHandle && $ req ->challenge === $ decodedClient ->challenge ) {
@@ -266,9 +264,11 @@ public function authenticate(array $requests, array $registrations, $response)
266264 U2FException::NO_MATCHING_REQUEST
267265 );
268266 }
267+
268+ // Check for a match for the response among a list of registrations
269269 foreach ($ registrations as $ reg ) {
270270 if ( !is_object ( $ reg ) ) {
271- throw new \InvalidArgumentException ('$registrations of authenticate() method only accepts array of object . ' );
271+ throw new \InvalidArgumentException ('$registrations of authenticate() method only accepts an array of objects . ' );
272272 }
273273
274274 if ($ reg ->keyHandle === $ response ->keyHandle ) {
@@ -282,20 +282,24 @@ public function authenticate(array $requests, array $registrations, $response)
282282 U2FException::NO_MATCHING_REGISTRATION
283283 );
284284 }
285- $ pemKey = $ this ->publicKeyToPem ($ this ->base64u_decode ($ reg ->publicKey ));
285+
286+ // On Success, check we have a valid public key
287+ $ pemKey = static ::publicKeyToPem (static ::base64u_decode ($ reg ->publicKey ));
286288 if ($ pemKey === null ) {
287289 throw new U2FException (
288290 'Decoding of public key failed ' ,
289291 U2FException::PUBKEY_DECODE
290292 );
291293 }
292294
293- $ signData = $ this ->base64u_decode ($ response ->signatureData );
295+ // Build signature and data from response
296+ $ signData = static ::base64u_decode ($ response ->signatureData );
294297 $ dataToVerify = hash ('sha256 ' , $ req ->appId , true );
295298 $ dataToVerify .= substr ($ signData , 0 , 5 );
296299 $ dataToVerify .= hash ('sha256 ' , $ clientData , true );
297300 $ signature = substr ($ signData , 5 );
298301
302+ // Verify the response data against the public key
299303 if (openssl_verify ($ dataToVerify , $ signature , $ pemKey , 'sha256 ' ) === 1 ) {
300304 $ ctr = unpack ("Nctr " , substr ($ signData , 1 , 4 ));
301305 $ counter = $ ctr ['ctr ' ];
@@ -318,12 +322,13 @@ public function authenticate(array $requests, array $registrations, $response)
318322 }
319323
320324 /**
325+ * @param string $attestDir
321326 * @return array
322327 */
323- private function get_certs ()
328+ private static function get_certs ($ attestDir )
324329 {
325330 $ files = [];
326- $ dir = $ this -> attestDir ;
331+ $ dir = $ attestDir ;
327332 if ($ dir && $ handle = opendir ($ dir )) {
328333 while (false !== ($ entry = readdir ($ handle ))) {
329334 if (is_file ("$ dir/ $ entry " )) {
@@ -339,7 +344,7 @@ private function get_certs()
339344 * @param string $data
340345 * @return string
341346 */
342- private function base64u_encode ($ data )
347+ private static function base64u_encode ($ data )
343348 {
344349 return trim (strtr (base64_encode ($ data ), '+/ ' , '-_ ' ), '= ' );
345350 }
@@ -348,7 +353,7 @@ private function base64u_encode($data)
348353 * @param string $data
349354 * @return string
350355 */
351- private function base64u_decode ($ data )
356+ private static function base64u_decode ($ data )
352357 {
353358 return base64_decode (strtr ($ data , '-_ ' , '+/ ' ));
354359 }
@@ -357,7 +362,7 @@ private function base64u_decode($data)
357362 * @param string $key
358363 * @return null|string
359364 */
360- private function publicKeyToPem ($ key )
365+ private static function publicKeyToPem ($ key )
361366 {
362367 if (strlen ($ key ) !== U2F ::PUBKEY_LEN || $ key [0 ] !== "\x04" ) {
363368 return null ;
@@ -388,7 +393,7 @@ private function publicKeyToPem($key)
388393 * @return string
389394 * @throws U2FException
390395 */
391- private function createChallenge ()
396+ private static function createChallenge ()
392397 {
393398 $ challenge = openssl_random_pseudo_bytes (32 , $ crypto_strong );
394399 if ( $ crypto_strong !== true ) {
@@ -398,7 +403,7 @@ private function createChallenge()
398403 );
399404 }
400405
401- $ challenge = $ this -> base64u_encode ( $ challenge );
406+ $ challenge = static :: base64u_encode ( $ challenge );
402407
403408 return $ challenge ;
404409 }
@@ -409,9 +414,9 @@ private function createChallenge()
409414 * @param string $cert
410415 * @return mixed
411416 */
412- private function fixSignatureUnusedBits ($ cert )
417+ private static function fixSignatureUnusedBits ($ cert )
413418 {
414- if (in_array (hash ('sha256 ' , $ cert ), $ this -> FIXCERTS )) {
419+ if (in_array (hash ('sha256 ' , $ cert ), static :: $ FIXCERTS )) {
415420 $ cert [strlen ($ cert ) - 257 ] = "\0" ;
416421 }
417422 return $ cert ;
0 commit comments