1
1
/*
2
- * Copyright (C) 2020-2023 Yubico.
2
+ * Copyright (C) 2020-2024 Yubico.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
@@ -105,6 +105,20 @@ boolean supportsEpForRpId(@Nullable String rpId) {
105
105
}
106
106
}
107
107
108
+ private static class AuthParams {
109
+ @ Nullable
110
+ private final byte [] pinUvAuthParam ;
111
+ @ Nullable
112
+ private final Integer pinUvAuthProtocol ;
113
+
114
+ AuthParams (
115
+ @ Nullable byte [] pinUvAuthParam ,
116
+ @ Nullable Integer pinUvAuthProtocol ) {
117
+ this .pinUvAuthParam = pinUvAuthParam ;
118
+ this .pinUvAuthProtocol = pinUvAuthProtocol ;
119
+ }
120
+ }
121
+
108
122
public BasicWebAuthnClient (Ctap2Session session ) throws IOException , CommandException {
109
123
this .ctap = session ;
110
124
Ctap2Session .InfoData info = ctap .getInfo ();
@@ -380,101 +394,86 @@ protected Ctap2Session.CredentialData ctapMakeCredential(
380
394
381
395
final SerializationType serializationType = SerializationType .CBOR ;
382
396
383
- byte [] pinToken = null ;
384
- try {
385
- if (options .getExtensions () != null ) {
386
- throw new ClientError (
387
- ClientError .Code .CONFIGURATION_UNSUPPORTED ,
388
- "Extensions not supported" );
389
- }
397
+ if (options .getExtensions () != null ) {
398
+ throw new ClientError (
399
+ ClientError .Code .CONFIGURATION_UNSUPPORTED ,
400
+ "Extensions not supported" );
401
+ }
390
402
391
- Map <String , ?> rp = options .getRp ().toMap (serializationType );
392
- String rpId = options .getRp ().getId ();
393
- if (rpId == null ) {
394
- ((Map <String , Object >) rp ).put ("id" , effectiveDomain );
395
- } else if (!(effectiveDomain .equals (rpId ) || effectiveDomain .endsWith ("." + rpId ))) {
396
- throw new ClientError (
397
- ClientError .Code .BAD_REQUEST ,
398
- "RP ID is not valid for effective domain" );
399
- }
403
+ Map <String , ?> rp = options .getRp ().toMap (serializationType );
404
+ String rpId = options .getRp ().getId ();
405
+ if (rpId == null ) {
406
+ ((Map <String , Object >) rp ).put ("id" , effectiveDomain );
407
+ } else if (!(effectiveDomain .equals (rpId ) || effectiveDomain .endsWith ("." + rpId ))) {
408
+ throw new ClientError (
409
+ ClientError .Code .BAD_REQUEST ,
410
+ "RP ID is not valid for effective domain" );
411
+ }
400
412
401
- byte [] pinUvAuthParam = null ;
402
- int pinUvAuthProtocol = 0 ;
403
-
404
- Map <String , Boolean > ctapOptions = new HashMap <>();
405
- AuthenticatorSelectionCriteria authenticatorSelection =
406
- options .getAuthenticatorSelection ();
407
- if (authenticatorSelection != null ) {
408
- String residentKeyRequirement = authenticatorSelection .getResidentKey ();
409
- if (ResidentKeyRequirement .REQUIRED .equals (residentKeyRequirement ) ||
410
- (ResidentKeyRequirement .PREFERRED .equals (residentKeyRequirement ) &&
411
- (pinSupported || uvSupported )
412
- )
413
- ) {
414
- ctapOptions .put (OPTION_RESIDENT_KEY , true );
415
- }
416
- if (getCtapUv (authenticatorSelection .getUserVerification (), pin != null )) {
417
- ctapOptions .put (OPTION_USER_VERIFICATION , true );
418
- }
419
- } else {
420
- if (getCtapUv (UserVerificationRequirement .PREFERRED , pin != null )) {
421
- ctapOptions .put (OPTION_USER_VERIFICATION , true );
422
- }
413
+ Map <String , Boolean > ctapOptions = new HashMap <>();
414
+ AuthenticatorSelectionCriteria authenticatorSelection =
415
+ options .getAuthenticatorSelection ();
416
+ if (authenticatorSelection != null ) {
417
+ String residentKeyRequirement = authenticatorSelection .getResidentKey ();
418
+ if (ResidentKeyRequirement .REQUIRED .equals (residentKeyRequirement ) ||
419
+ (ResidentKeyRequirement .PREFERRED .equals (residentKeyRequirement ) &&
420
+ (pinSupported || uvSupported )
421
+ )
422
+ ) {
423
+ ctapOptions .put (OPTION_RESIDENT_KEY , true );
423
424
}
424
-
425
- if (pin != null ) {
426
- pinToken = clientPin .getPinToken (pin , ClientPin .PIN_PERMISSION_MC , rpId );
427
- pinUvAuthParam = clientPin .getPinUvAuth ().authenticate (pinToken , clientDataHash );
428
- pinUvAuthProtocol = clientPin .getPinUvAuth ().getVersion ();
429
- } else if (pinConfigured && ctapOptions .containsKey (OPTION_USER_VERIFICATION )) {
430
- pinToken = clientPin .getUvToken (ClientPin .PIN_PERMISSION_MC , rpId , null );
431
- pinUvAuthParam = clientPin .getPinUvAuth ().authenticate (pinToken , clientDataHash );
432
- pinUvAuthProtocol = clientPin .getPinUvAuth ().getVersion ();
433
- } else if (pinConfigured && !ctapOptions .containsKey (OPTION_USER_VERIFICATION )) {
434
- throw new PinRequiredClientError ();
425
+ if (getCtapUv (authenticatorSelection .getUserVerification (), pin != null )) {
426
+ ctapOptions .put (OPTION_USER_VERIFICATION , true );
427
+ }
428
+ } else {
429
+ if (getCtapUv (UserVerificationRequirement .PREFERRED , pin != null )) {
430
+ ctapOptions .put (OPTION_USER_VERIFICATION , true );
435
431
}
432
+ }
436
433
437
- final List <PublicKeyCredentialDescriptor > excludeCredentials =
438
- removeUnsupportedCredentials (
439
- options .getExcludeCredentials ()
440
- );
434
+ final AuthParams authParams = getAuthParams (
435
+ clientDataHash ,
436
+ ctapOptions .containsKey (OPTION_USER_VERIFICATION ),
437
+ pin ,
438
+ ClientPin .PIN_PERMISSION_MC ,
439
+ rpId );
441
440
442
- final Map <String , ?> user = options .getUser ().toMap (serializationType );
441
+ final List <PublicKeyCredentialDescriptor > excludeCredentials =
442
+ removeUnsupportedCredentials (
443
+ options .getExcludeCredentials ()
444
+ );
443
445
444
- List <Map <String , ?>> pubKeyCredParams = new ArrayList <>();
445
- for (PublicKeyCredentialParameters param : options .getPubKeyCredParams ()) {
446
- if (isPublicKeyCredentialTypeSupported (param .getType ())) {
447
- pubKeyCredParams .add (param .toMap (serializationType ));
448
- }
449
- }
446
+ final Map <String , ?> user = options .getUser ().toMap (serializationType );
450
447
451
- @ Nullable Integer validatedEnterpriseAttestation = null ;
452
- if (isEnterpriseAttestationSupported () &&
453
- AttestationConveyancePreference .ENTERPRISE .equals (options .getAttestation ()) &&
454
- userAgentConfiguration .supportsEpForRpId (rpId ) &&
455
- enterpriseAttestation != null &&
456
- (enterpriseAttestation == 1 || enterpriseAttestation == 2 )) {
457
- validatedEnterpriseAttestation = enterpriseAttestation ;
448
+ List <Map <String , ?>> pubKeyCredParams = new ArrayList <>();
449
+ for (PublicKeyCredentialParameters param : options .getPubKeyCredParams ()) {
450
+ if (isPublicKeyCredentialTypeSupported (param .getType ())) {
451
+ pubKeyCredParams .add (param .toMap (serializationType ));
458
452
}
453
+ }
459
454
460
- return ctap .makeCredential (
461
- clientDataHash ,
462
- rp ,
463
- user ,
464
- pubKeyCredParams ,
465
- getCredentialList (excludeCredentials ),
466
- null ,
467
- ctapOptions .isEmpty () ? null : ctapOptions ,
468
- pinUvAuthParam ,
469
- pinUvAuthProtocol ,
470
- validatedEnterpriseAttestation ,
471
- state
472
- );
473
- } finally {
474
- if (pinToken != null ) {
475
- Arrays .fill (pinToken , (byte ) 0 );
476
- }
455
+ @ Nullable Integer validatedEnterpriseAttestation = null ;
456
+ if (isEnterpriseAttestationSupported () &&
457
+ AttestationConveyancePreference .ENTERPRISE .equals (options .getAttestation ()) &&
458
+ userAgentConfiguration .supportsEpForRpId (rpId ) &&
459
+ enterpriseAttestation != null &&
460
+ (enterpriseAttestation == 1 || enterpriseAttestation == 2 )) {
461
+ validatedEnterpriseAttestation = enterpriseAttestation ;
477
462
}
463
+
464
+ return ctap .makeCredential (
465
+ clientDataHash ,
466
+ rp ,
467
+ user ,
468
+ pubKeyCredParams ,
469
+ getCredentialList (excludeCredentials ),
470
+ null ,
471
+ ctapOptions .isEmpty () ? null : ctapOptions ,
472
+ authParams .pinUvAuthParam ,
473
+ authParams .pinUvAuthProtocol ,
474
+ validatedEnterpriseAttestation ,
475
+ state
476
+ );
478
477
}
479
478
480
479
/**
@@ -519,20 +518,14 @@ protected List<Ctap2Session.AssertionData> ctapGetAssertions(
519
518
throw new ClientError (ClientError .Code .CONFIGURATION_UNSUPPORTED , "Extensions not supported" );
520
519
}
521
520
522
- byte [] pinUvAuthParam = null ;
523
- int pinUvAuthProtocol = 0 ;
524
- byte [] pinToken = null ;
525
- try {
526
- if (pin != null ) {
527
- pinToken = clientPin .getPinToken (pin , ClientPin .PIN_PERMISSION_GA , rpId );
528
- pinUvAuthParam = clientPin .getPinUvAuth ().authenticate (pinToken , clientDataHash );
529
- pinUvAuthProtocol = clientPin .getPinUvAuth ().getVersion ();
530
- } else if (pinConfigured && ctapOptions .containsKey (OPTION_USER_VERIFICATION )) {
531
- pinToken = clientPin .getUvToken (ClientPin .PIN_PERMISSION_GA , rpId , null );
532
- pinUvAuthParam = clientPin .getPinUvAuth ().authenticate (pinToken , clientDataHash );
533
- pinUvAuthProtocol = clientPin .getPinUvAuth ().getVersion ();
534
- }
521
+ final AuthParams authParams = getAuthParams (
522
+ clientDataHash ,
523
+ ctapOptions .containsKey (OPTION_USER_VERIFICATION ),
524
+ pin ,
525
+ ClientPin .PIN_PERMISSION_GA ,
526
+ rpId );
535
527
528
+ try {
536
529
final List <PublicKeyCredentialDescriptor > allowCredentials = removeUnsupportedCredentials (
537
530
options .getAllowCredentials ()
538
531
);
@@ -543,19 +536,15 @@ protected List<Ctap2Session.AssertionData> ctapGetAssertions(
543
536
getCredentialList (allowCredentials ),
544
537
null ,
545
538
ctapOptions .isEmpty () ? null : ctapOptions ,
546
- pinUvAuthParam ,
547
- pinUvAuthProtocol ,
539
+ authParams . pinUvAuthParam ,
540
+ authParams . pinUvAuthProtocol ,
548
541
state
549
542
);
550
543
} catch (CtapException e ) {
551
544
if (e .getCtapError () == CtapException .ERR_PIN_INVALID ) {
552
545
throw new PinInvalidClientError (e , clientPin .getPinRetries ().getCount ());
553
546
}
554
547
throw ClientError .wrapCtapException (e );
555
- } finally {
556
- if (pinToken != null ) {
557
- Arrays .fill (pinToken , (byte ) 0 );
558
- }
559
548
}
560
549
}
561
550
@@ -603,6 +592,47 @@ private boolean getCtapUv(String userVerification, boolean pinProvided) throws C
603
592
}
604
593
}
605
594
595
+ private AuthParams getAuthParams (
596
+ byte [] clientDataHash ,
597
+ boolean shouldUv ,
598
+ @ Nullable char [] pin ,
599
+ @ Nullable Integer permissions ,
600
+ @ Nullable String rpId
601
+ ) throws ClientError , IOException , CommandException {
602
+ @ Nullable byte [] authToken = null ;
603
+ @ Nullable byte [] authParam = null ;
604
+ @ Nullable Integer authProtocolVersion = null ;
605
+
606
+ try {
607
+ if (pin != null ) {
608
+ authToken = clientPin .getPinToken (pin , permissions , rpId );
609
+ authParam = clientPin .getPinUvAuth ().authenticate (authToken , clientDataHash );
610
+ authProtocolVersion = clientPin .getPinUvAuth ().getVersion ();
611
+ } else if (pinConfigured ) {
612
+ if (shouldUv && uvConfigured ) {
613
+ if (ClientPin .isTokenSupported (ctap .getCachedInfo ())) {
614
+ authToken = clientPin .getUvToken (permissions , rpId , null );
615
+ authParam = clientPin .getPinUvAuth ().authenticate (authToken , clientDataHash );
616
+ authProtocolVersion = clientPin .getPinUvAuth ().getVersion ();
617
+ }
618
+ // no authToken is created means that internal UV is used
619
+ } else {
620
+ // the authenticator supports pin but no PIN was provided
621
+ throw new PinRequiredClientError ();
622
+ }
623
+ }
624
+ return new AuthParams (
625
+ authParam ,
626
+ authProtocolVersion
627
+ );
628
+
629
+ } finally {
630
+ if (authToken != null ) {
631
+ Arrays .fill (authToken , (byte ) 0 );
632
+ }
633
+ }
634
+ }
635
+
606
636
/**
607
637
* Calculates the preferred pinUvAuth protocol for authenticator provided list.
608
638
* Returns PinUvAuthDummyProtocol if the authenticator does not support any of the SDK
0 commit comments