17
17
import com .yubico .webauthn .RelyingParty ;
18
18
import com .yubico .webauthn .StartAssertionOptions ;
19
19
import com .yubico .webauthn .StartRegistrationOptions ;
20
+ import com .yubico .webauthn .attestation .Attestation ;
20
21
import com .yubico .webauthn .attestation .MetadataResolver ;
21
22
import com .yubico .webauthn .attestation .MetadataService ;
22
23
import com .yubico .webauthn .attestation .StandardMetadataService ;
25
26
import com .yubico .webauthn .attestation .resolver .SimpleResolverWithEquality ;
26
27
import com .yubico .webauthn .data .AssertionResult ;
27
28
import com .yubico .webauthn .data .AttestationConveyancePreference ;
29
+ import com .yubico .webauthn .data .AttestationType ;
28
30
import com .yubico .webauthn .data .ByteArray ;
31
+ import com .yubico .webauthn .data .PublicKeyCredentialDescriptor ;
29
32
import com .yubico .webauthn .data .PublicKeyCredentialParameters ;
30
33
import com .yubico .webauthn .data .RegistrationResult ;
31
34
import com .yubico .webauthn .data .RelyingPartyIdentity ;
39
42
import demo .webauthn .data .CredentialRegistration ;
40
43
import demo .webauthn .data .RegistrationRequest ;
41
44
import demo .webauthn .data .RegistrationResponse ;
45
+ import demo .webauthn .data .U2fRegistrationResponse ;
42
46
import java .io .IOException ;
43
47
import java .io .InputStream ;
44
48
import java .io .InputStreamReader ;
49
+ import java .security .cert .CertificateEncodingException ;
45
50
import java .security .cert .CertificateException ;
46
51
import java .security .cert .X509Certificate ;
47
52
import java .time .Clock ;
@@ -230,6 +235,16 @@ public SuccessfulRegistrationResult(RegistrationRequest request, RegistrationRes
230
235
}
231
236
}
232
237
238
+ @ Value
239
+ public class SuccessfulU2fRegistrationResult {
240
+ final boolean success = true ;
241
+ final RegistrationRequest request ;
242
+ final U2fRegistrationResponse response ;
243
+ final CredentialRegistration registration ;
244
+ boolean attestationTrusted ;
245
+ Optional <AttestationCertInfo > attestationCert ;
246
+ }
247
+
233
248
@ Value
234
249
public static class AttestationCertInfo {
235
250
final ByteArray der ;
@@ -298,6 +313,76 @@ public Either<List<String>, SuccessfulRegistrationResult> finishRegistration(Str
298
313
}
299
314
}
300
315
316
+ /**
317
+ * NOTE: This method does not validate the result. This sole purpose of
318
+ * this feature is to enable testing that the "appid" extension works. This
319
+ * requires a credential registered via the U2F API instead of the WebAuthn
320
+ * API. Since WebAuthn is backwards compatible only with U2F
321
+ * authentication, not registration, this method is used to sidestep the
322
+ * WebAuthn module and just add the credential to the database of
323
+ * registrations.
324
+ *
325
+ * This is NOT an example of good code. DO NOT base any code off this as an
326
+ * example.
327
+ */
328
+ public Either <List <String >, SuccessfulU2fRegistrationResult > insecureFinishU2fRegistration (String responseJson ) {
329
+ logger .trace ("insecureFinishU2fRegistration responseJson: {}" , responseJson );
330
+ U2fRegistrationResponse response = null ;
331
+ try {
332
+ response = jsonMapper .readValue (responseJson , U2fRegistrationResponse .class );
333
+ } catch (IOException e ) {
334
+ logger .error ("JSON error in insecureFinishU2fRegistration; responseJson: {}" , responseJson , e );
335
+ return Either .left (Arrays .asList ("Registration failed!" , "Failed to decode response object." , e .getMessage ()));
336
+ }
337
+
338
+ RegistrationRequest request = registerRequestStorage .getIfPresent (response .getRequestId ());
339
+ registerRequestStorage .invalidate (response .getRequestId ());
340
+
341
+ if (request == null ) {
342
+ logger .debug ("fail insecureFinishU2fRegistration responseJson: {}" , responseJson );
343
+ return Either .left (Arrays .asList ("Registration failed!" , "No such registration in progress." ));
344
+ } else {
345
+ X509Certificate attestationCert = null ;
346
+ try {
347
+ attestationCert = CertificateParser .parseDer (response .getCredential ().getU2fResponse ().getAttestationCert ().getBytes ());
348
+ } catch (CertificateException e ) {
349
+ logger .error ("Failed to parse attestation certificate: {}" , response .getCredential ().getU2fResponse ().getAttestationCert (), e );
350
+ }
351
+
352
+ Optional <Attestation > attestation = Optional .empty ();
353
+ try {
354
+ if (attestationCert != null ) {
355
+ attestation = Optional .of (metadataService .getAttestation (Collections .singletonList (attestationCert )));
356
+ }
357
+ } catch (CertificateEncodingException e ) {
358
+ logger .error ("Failed to resolve attestation" , e );
359
+ }
360
+
361
+ final RegistrationResult result = RegistrationResult .builder ()
362
+ .attestationMetadata (attestation )
363
+ .attestationTrusted (attestation .map (Attestation ::isTrusted ).orElse (false ))
364
+ .attestationType (AttestationType .BASIC )
365
+ .keyId (PublicKeyCredentialDescriptor .builder ().id (response .getCredential ().getU2fResponse ().getKeyHandle ()).build ())
366
+ .publicKeyCose (WebAuthnCodecs .rawEcdaKeyToCose (response .getCredential ().getU2fResponse ().getPublicKey ()))
367
+ .build ();
368
+
369
+ return Either .right (
370
+ new SuccessfulU2fRegistrationResult (
371
+ request ,
372
+ response ,
373
+ addRegistration (
374
+ request .getPublicKeyCredentialCreationOptions ().getUser (),
375
+ request .getCredentialNickname (),
376
+ 0 ,
377
+ result
378
+ ),
379
+ result .isAttestationTrusted (),
380
+ Optional .of (new AttestationCertInfo (response .getCredential ().getU2fResponse ().getAttestationCert ()))
381
+ )
382
+ );
383
+ }
384
+ }
385
+
301
386
public Either <List <String >, AssertionRequest > startAuthentication (Optional <String > username ) {
302
387
logger .trace ("startAuthentication username: {}" , username );
303
388
@@ -457,13 +542,27 @@ private CredentialRegistration addRegistration(
457
542
Optional <String > nickname ,
458
543
RegistrationResponse response ,
459
544
RegistrationResult registration
545
+ ) {
546
+ return addRegistration (
547
+ userIdentity ,
548
+ nickname ,
549
+ response .getCredential ().getResponse ().getAttestation ().getAuthenticatorData ().getSignatureCounter (),
550
+ registration
551
+ );
552
+ }
553
+
554
+ private CredentialRegistration addRegistration (
555
+ UserIdentity userIdentity ,
556
+ Optional <String > nickname ,
557
+ long signatureCount ,
558
+ RegistrationResult registration
460
559
) {
461
560
CredentialRegistration reg = CredentialRegistration .builder ()
462
561
.userIdentity (userIdentity )
463
562
.credentialNickname (nickname )
464
563
.registrationTime (clock .instant ())
465
564
.registration (registration )
466
- .signatureCount (response . getCredential (). getResponse (). getAttestation (). getAuthenticatorData (). getSignatureCounter () )
565
+ .signatureCount (signatureCount )
467
566
.build ();
468
567
469
568
logger .debug (
@@ -477,4 +576,5 @@ private CredentialRegistration addRegistration(
477
576
userStorage .addRegistrationByUsername (userIdentity .getName (), reg );
478
577
return reg ;
479
578
}
579
+
480
580
}
0 commit comments