diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e2bbe639e..fbdaee05c 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -3,7 +3,9 @@ name: Test coverage on: push: - branches: [main] + branches: + - main + - 'release-*' jobs: test: @@ -45,6 +47,7 @@ jobs: sed "s/{shortcommit}/${GITHUB_SHA:0:8}/g;s/{commit}/${GITHUB_SHA}/g;s#{repo}#${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}#g" .github/workflows/coverage/index.html.template > build/gh-pages/index.html - name: Create coverage badge + if: ${{ github.ref == 'refs/heads/main' }} # This creates a file that defines a [Shields.io endpoint badge](https://shields.io/endpoint) # which we can then include in the project README. uses: ./.github/actions/pit-results-badge @@ -69,6 +72,7 @@ jobs: prev-mutations-file: prev-mutations.xml - name: Push to GitHub Pages + if: ${{ github.ref == 'refs/heads/main' }} run: | git config user.name github-actions git config user.email github-actions@github.com diff --git a/NEWS b/NEWS index 979a4eafd..4636e0c1a 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,11 @@ +== Version 2.5.1 == + +Changes: + +* Dropped dependency on COSE-Java. +* Fixed incompatibility with Jackson version 2.17.0-rc1. + + == Version 2.5.0 == `webauthn-server-core`: diff --git a/README b/README index 88fae3067..2e779d00b 100644 --- a/README +++ b/README @@ -64,7 +64,7 @@ Maven: com.yubico webauthn-server-core - 2.5.0 + 2.5.1 compile ---------- @@ -72,7 +72,7 @@ Maven: Gradle: ---------- -implementation("com.yubico:webauthn-server-core:2.5.0") +implementation("com.yubico:webauthn-server-core:2.5.1") ---------- NOTE: You may need additional dependencies with JCA providers to support some signature algorithms. @@ -85,7 +85,7 @@ The library will log warnings if you try to configure it for algorithms with no This library uses link:https://semver.org/[semantic versioning]. The public API consists of all public classes, methods and fields in the `com.yubico.webauthn` package and its subpackages, i.e., everything covered by the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/package-summary.html[Javadoc], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/package-summary.html[Javadoc], *with the exception* of features annotated with a `@Deprecated` annotation and a `@deprecated EXPERIMENTAL:` tag in JavaDoc. Such features are considered unstable and may receive breaking changes without a @@ -108,7 +108,7 @@ In addition to the main `webauthn-server-core` module, there is also: == Documentation See the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/package-summary.html[Javadoc] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/package-summary.html[Javadoc] for in-depth API documentation. @@ -118,20 +118,20 @@ Using this library comes in two parts: the server side and the client side. The server side involves: 1. Implement the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] interface with your database access logic. 2. Instantiate the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class. 3. Use the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#startRegistration(com.yubico.webauthn.StartRegistrationOptions)[`RelyingParty.startRegistration(...)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#startRegistration(com.yubico.webauthn.StartRegistrationOptions)[`RelyingParty.startRegistration(...)`] and - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] methods to perform registration ceremonies. 4. Use the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`] and - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] methods to perform authentication ceremonies. 5. Optionally use additional features: passkeys, passwordless multi-factor authentication, credential backup state. @@ -151,7 +151,7 @@ link:webauthn-server-demo[`webauthn-server-demo`] for a complete demo server. === 1. Implement a `CredentialRepository` The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] interface abstracts your database in a database-agnostic way. The concrete implementation will be different for every project, but you can use link:https://github.com/Yubico/java-webauthn-server/blob/main/webauthn-server-demo/src/main/java/demo/webauthn/InMemoryRegistrationStorage.java[`InMemoryRegistrationStorage`] @@ -160,11 +160,11 @@ as a simple example. === 2. Instantiate a `RelyingParty` The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class is the main entry point to the library. You can instantiate it using its builder methods, passing in your -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] implementation (called `MyCredentialRepository` here) as an argument: [source,java] @@ -186,7 +186,7 @@ RelyingParty rp = RelyingParty.builder() A registration ceremony consists of 5 main steps: 1. Generate registration parameters using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#startRegistration(com.yubico.webauthn.StartRegistrationOptions)[`RelyingParty.startRegistration(...)`]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#startRegistration(com.yubico.webauthn.StartRegistrationOptions)[`RelyingParty.startRegistration(...)`]. 2. Send registration parameters to the client and call https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create[`navigator.credentials.create()`]. 3. With `cred` as the result of the successfully resolved promise, @@ -194,7 +194,7 @@ A registration ceremony consists of 5 main steps: and https://www.w3.org/TR/webauthn-2/#ref-for-dom-authenticatorattestationresponse-gettransports[`cred.response.getTransports()`] and return their results along with `cred` to the server. 4. Validate the response using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`]. 5. Update your database using the `finishRegistration` output. This example uses GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library to do both (2) and (3) in one function call. @@ -226,15 +226,15 @@ return credentialCreateJson; // Send to client ---------- You will need to keep this -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html[`PublicKeyCredentialCreationOptions`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html[`PublicKeyCredentialCreationOptions`] object in temporary storage so you can also pass it into -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] later. If needed, you can use the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#toJson()[`toJson()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#toJson()[`toJson()`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#fromJson(java.lang.String)[`fromJson(String)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.html#fromJson(java.lang.String)[`fromJson(String)`] methods to serialize and deserialize the value for storage. Now call the WebAuthn API on the client side: @@ -295,7 +295,7 @@ storeCredential( // Some database access method of your own design Like registration ceremonies, an authentication ceremony consists of 5 main steps: 1. Generate authentication parameters using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`]. 2. Send authentication parameters to the client, call https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get[`navigator.credentials.get()`] and return the response. @@ -303,7 +303,7 @@ Like registration ceremonies, an authentication ceremony consists of 5 main step https://www.w3.org/TR/webauthn-2/#ref-for-dom-publickeycredential-getclientextensionresults[`cred.getClientExtensionResults()`] and return the result along with `cred` to the server. 4. Validate the response using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`]. 5. Update your database using the `finishAssertion` output, and act upon the result (for example, grant login access). This example uses GitHub's link:https://github.com/github/webauthn-json[webauthn-json] library to do both (2) and (3) in one function call. @@ -320,15 +320,15 @@ return credentialGetJson; // Send to client ---------- Again, you will need to keep this -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/AssertionRequest.html[`AssertionRequest`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/AssertionRequest.html[`AssertionRequest`] object in temporary storage so you can also pass it into -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] later. If needed, you can use the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/AssertionRequest.html#toJson()[`toJson()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/AssertionRequest.html#toJson()[`toJson()`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/AssertionRequest.html#fromJson(java.lang.String)[`fromJson(String)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/AssertionRequest.html#fromJson(java.lang.String)[`fromJson(String)`] methods to serialize and deserialize the value for storage. Now call the WebAuthn API on the client side: @@ -369,7 +369,7 @@ throw new RuntimeException("Authentication failed"); ---------- Finally, if the previous step was successful, update your database using the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. Most importantly, you should update the signature counter. That might look something like this: [source,java] @@ -420,9 +420,9 @@ Many passkey-capable authenticators also offer a credential sync mechanism to allow one passkey to be used on multiple devices. Passkeys can be created by setting the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/StartRegistrationOptions.StartRegistrationOptionsBuilder.html#authenticatorSelection(com.yubico.webauthn.data.AuthenticatorSelectionCriteria)[`authenticatorSelection`].link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/StartRegistrationOptions.StartRegistrationOptionsBuilder.html#authenticatorSelection(com.yubico.webauthn.data.AuthenticatorSelectionCriteria)[`authenticatorSelection`].link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`] option to -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/ResidentKeyRequirement.html#REQUIRED[`REQUIRED`]: +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/ResidentKeyRequirement.html#REQUIRED[`REQUIRED`]: [source,java] ---------- @@ -444,15 +444,15 @@ AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder().bui Some authenticators might create passkeys even if not required, and setting the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/AuthenticatorSelectionCriteria.AuthenticatorSelectionCriteriaBuilder.html#residentKey(com.yubico.webauthn.data.ResidentKeyRequirement)[`residentKey`] option to -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/ResidentKeyRequirement.html#PREFERRED[`PREFERRED`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/ResidentKeyRequirement.html#PREFERRED[`PREFERRED`] will create a passkey if the authenticator supports it. The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegistrationResult.html#isDiscoverable()[`RegistrationResult.isDiscoverable()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegistrationResult.html#isDiscoverable()[`RegistrationResult.isDiscoverable()`] method can be used to determine whether the created credential is a passkey. This requires the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/RegistrationExtensionInputs.RegistrationExtensionInputsBuilder.html#credProps()[`credProps` extension] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/RegistrationExtensionInputs.RegistrationExtensionInputsBuilder.html#credProps()[`credProps` extension] to be enabled, which it is by default. @@ -471,7 +471,7 @@ AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder() ---------- Then -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] will enforce that user verification was performed. However, there is no guarantee that the user's authenticator will support this unless the user has some credential created with the @@ -508,14 +508,14 @@ AssertionRequest request = rp.startAssertion(StartAssertionOptions.builder() ---------- In this case -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#finishRegistration(com.yubico.webauthn.FinishRegistrationOptions)[`RelyingParty.finishRegistration(...)`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)[`RelyingParty.finishAssertion(...)`] will NOT enforce user verification, but instead the `isUserVerified()` method of -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/AssertionResult.html[`AssertionResult`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/AssertionResult.html[`AssertionResult`] will tell whether user verification was used. For example, you could prompt for a password as the second factor if `isUserVerified()` returns `false`: @@ -555,7 +555,7 @@ In particular you need to: - Add the credential request option `mediation: "conditional"` alongside the `publicKey` option generated by -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html#startAssertion(com.yubico.webauthn.StartAssertionOptions)[`RelyingParty.startAssertion(...)`], - Add `autocomplete="username webauthn"` to a username input field on the page, and - Call `navigator.credentials.get()` in the background. @@ -574,9 +574,9 @@ Some authenticators may allow credentials to be backed up and/or synced between This capability and its current state is signaled via the link:https://w3c.github.io/webauthn/#sctn-credential-backup[Credential Backup State] flags, which are available via the `isBackedUp()` and `isBackupEligible()` methods of -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/AssertionResult.html[`AssertionResult`]. These can be used as a hint about how vulnerable a user is to authenticator loss. In particular, a user with only one credential which is not backed up may risk getting locked out if they lose their authenticator. @@ -600,14 +600,14 @@ To migrate to using the WebAuthn API, you need to do the following: 1. Follow the link:#getting-started[Getting started] guide above to set up WebAuthn support in general. + -Note that unlike a U2F AppID, the WebAuthn link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/RelyingPartyIdentity.RelyingPartyIdentityBuilder.html#id(java.lang.String)[RP ID] +Note that unlike a U2F AppID, the WebAuthn link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/RelyingPartyIdentity.RelyingPartyIdentityBuilder.html#id(java.lang.String)[RP ID] consists of only the domain name of the AppID. WebAuthn does not support link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-appid-and-facets-v1.2-ps-20170411.html[U2F Trusted Facet Lists]. 2. Set the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#appId(com.yubico.webauthn.extension.appid.AppId)[`appId()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#appId(com.yubico.webauthn.extension.appid.AppId)[`appId()`] setting on your - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] instance. The argument to the `appid()` setting should be the same as you used for the `appId` argument to the link:https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-javascript-api-v1.2-ps-20170411.html#high-level-javascript-api[U2F `register` and `sign` functions]. @@ -625,22 +625,22 @@ extensions and configure the `RelyingParty` to accept the given AppId when verif privacy consideration. 4. When your - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] creates a - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegisteredCredential.html[`RegisteredCredential`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegisteredCredential.html[`RegisteredCredential`] for a U2F credential, use the U2F key handle as the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#credentialId(com.yubico.webauthn.data.ByteArray)[credential ID]. + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#credentialId(com.yubico.webauthn.data.ByteArray)[credential ID]. If you store key handles base64 encoded, you should decode them using - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/ByteArray.html#fromBase64(java.lang.String)[`ByteArray.fromBase64`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/ByteArray.html#fromBase64(java.lang.String)[`ByteArray.fromBase64`] or - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/data/ByteArray.html#fromBase64Url(java.lang.String)[`ByteArray.fromBase64Url`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/data/ByteArray.html#fromBase64Url(java.lang.String)[`ByteArray.fromBase64Url`] as appropriate before passing them to the `RegisteredCredential`. 5. When your `CredentialRepository` creates a `RegisteredCredential` for a U2F credential, use the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyEs256Raw(com.yubico.webauthn.data.ByteArray)[`publicKeyEs256Raw()`] - method instead of link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyCose(com.yubico.webauthn.data.ByteArray)[`publicKeyCose()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyEs256Raw(com.yubico.webauthn.data.ByteArray)[`publicKeyEs256Raw()`] + method instead of link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegisteredCredential.RegisteredCredentialBuilder.html#publicKeyCose(com.yubico.webauthn.data.ByteArray)[`publicKeyCose()`] to set the credential public key. 6. Replace calls to the U2F @@ -774,17 +774,17 @@ provides optional additional features for working with attestation. See the module documentation for more details. Alternatively, you can use the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] interface to implement your own source of attestation root certificates and set it as the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] for your -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] instance. Note that depending on your JCA provider configuration, you may need to set the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/attestation/AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder.html#enableRevocationChecking(boolean)[`enableRevocationChecking`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/attestation/AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder.html#enableRevocationChecking(boolean)[`enableRevocationChecking`] and/or -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/attestation/AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder.html#policyTreeValidator(java.util.function.Predicate)[`policyTreeValidator`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/attestation/AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder.html#policyTreeValidator(java.util.function.Predicate)[`policyTreeValidator`] settings for compatibility with some authenticators' attestation certificates. See the JavaDoc for these settings for more information. diff --git a/build.gradle b/build.gradle index d61a8e0bf..5bfbf93ac 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,6 @@ dependencies { constraints { api(constraintLibs.bundles.jackson) api(constraintLibs.cbor) - api(constraintLibs.cose) api(constraintLibs.guava) api(constraintLibs.httpclient5) api(constraintLibs.slf4j) diff --git a/doc/releasing.md b/doc/releasing.md index 88083b50c..3c2a46c88 100644 --- a/doc/releasing.md +++ b/doc/releasing.md @@ -6,13 +6,22 @@ Release candidate versions 1. Make sure release notes in `NEWS` are up to date. - 2. Run the tests one more time: + 2. Review the diff from the previous version for any changes to the public API, + and adjust the upcoming version number accordingly. + + If any implementation dependencies have been added to method signatures in + the public API, including `throws` declarations, change these dependencies + from `implementation` to `api` dependency declarations in the relevant + Gradle build script. Conversely, remove or downgrade to `implementation` any + dependencies no longer exposed in the public API. + + 3. Run the tests one more time: ``` $ ./gradlew clean check ``` - 3. Update the Java version in the [`release-verify-signatures` + 4. Update the Java version in the [`release-verify-signatures` workflow](https://github.com/Yubico/java-webauthn-server/blob/main/.github/workflows/release-verify-signatures.yml#L42). See the `openjdk version` line of output from `java -version`: @@ -34,7 +43,7 @@ Release candidate versions Commit this change, if any. - 4. Tag the head commit with an `X.Y.Z-RCN` tag: + 5. Tag the head commit with an `X.Y.Z-RCN` tag: ``` $ git tag -a -s 1.4.0-RC1 -m "Pre-release 1.4.0-RC1" @@ -42,13 +51,13 @@ Release candidate versions No tag body needed. - 5. Publish to Sonatype Nexus: + 6. Publish to Sonatype Nexus: ``` $ ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository ``` - 6. Push to GitHub. + 7. Push to GitHub. If the pre-release makes significant changes to the project README, such that the README does not accurately reflect the latest non-pre-release @@ -66,7 +75,7 @@ Release candidate versions $ git push origin main 1.4.0-RC1 ``` - 7. Make GitHub release. + 8. Make GitHub release. - Use the new tag as the release tag. - Check the pre-release checkbox. @@ -76,7 +85,7 @@ Release candidate versions - Note the JDK version shown by `java -version` in step 3. For example: `openjdk version "17.0.7" 2023-04-18`. - 8. Check that the ["Reproducible binary" + 9. Check that the ["Reproducible binary" workflow](https://github.com/Yubico/java-webauthn-server/actions/workflows/release-verify-signatures.yml) runs and succeeds. @@ -86,7 +95,16 @@ Release versions 1. Make sure release notes in `NEWS` are up to date. - 2. Make a no-fast-forward merge from the last (non release candidate) release + 2. Review the diff from the previous version for any changes to the public API, + and adjust the upcoming version number accordingly. + + If any implementation dependencies have been added to method signatures in + the public API, including `throws` declarations, change these dependencies + from `implementation` to `api` dependency declarations in the relevant + Gradle build script. Conversely, remove or downgrade to `implementation` any + dependencies no longer exposed in the public API. + + 3. Make a no-fast-forward merge from the last (non release candidate) release to the commit to be released: ``` @@ -108,13 +126,13 @@ Release versions $ git branch -d release-1.4.0 ``` - 3. Remove the "(unreleased)" tag from `NEWS`. + 4. Remove the "(unreleased)" tag from `NEWS`. - 4. Update the version in the dependency snippets in the README. + 5. Update the version in the dependency snippets in the README. - 5. Update the version in JavaDoc links in the READMEs. + 6. Update the version in JavaDoc links in the READMEs. - 6. Update the Java version in the [`release-verify-signatures` + 7. Update the Java version in the [`release-verify-signatures` workflow](https://github.com/Yubico/java-webauthn-server/blob/main/.github/workflows/release-verify-signatures.yml#L42). See the `openjdk version` line of output from `java -version`: @@ -134,20 +152,20 @@ Release versions java: ["17.0.7"] ``` - 7. Amend these changes into the merge commit: + 8. Amend these changes into the merge commit: ``` $ git add NEWS README */README .github/workflows/release-verify-signatures.yml $ git commit --amend --reset-author ``` - 8. Run the tests one more time: + 9. Run the tests one more time: ``` $ ./gradlew clean check ``` - 9. Tag the merge commit with an `X.Y.Z` tag: +10. Tag the merge commit with an `X.Y.Z` tag: ``` $ git tag -a -s 1.4.0 -m "Release 1.4.0" @@ -155,19 +173,19 @@ Release versions No tag body needed since that's included in the commit. -10. Publish to Sonatype Nexus: +11. Publish to Sonatype Nexus: ``` $ ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository ``` -11. Push to GitHub: +12. Push to GitHub: ``` $ git push origin main 1.4.0 ``` -12. Make GitHub release. +13. Make GitHub release. - Use the new tag as the release tag. - Copy the release notes from `NEWS` into the GitHub release notes; reformat @@ -176,6 +194,6 @@ Release versions - Note the JDK version shown by `java -version` in step 6. For example: `openjdk version "17.0.7" 2023-04-18`. -13. Check that the ["Reproducible binary" +14. Check that the ["Reproducible binary" workflow](https://github.com/Yubico/java-webauthn-server/actions/workflows/release-verify-signatures.yml) runs and succeeds. diff --git a/settings.gradle.kts b/settings.gradle.kts index 3cb500697..83a4d55e2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,6 @@ dependencyResolutionManagement { versionCatalogs { create("constraintLibs") { library("cbor", "com.upokecenter:cbor:[4.5.1,5)") - library("cose", "com.augustcellars.cose:cose-java:[1.0.0,2)") library("guava", "com.google.guava:guava:[24.1.1,33)") library("httpclient5", "org.apache.httpcomponents.client5:httpclient5:[5.0.0,6)") library("slf4j", "org.slf4j:slf4j-api:[1.7.25,3)") diff --git a/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/build.gradle.kts b/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/build.gradle.kts index 801446db1..3d5d07c94 100644 --- a/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/build.gradle.kts +++ b/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/build.gradle.kts @@ -12,9 +12,6 @@ dependencies { testImplementation("junit:junit:4.12") testImplementation("org.mockito:mockito-core:[2.27.0,3)") - // Runtime-only internal dependency of webauthn-server-core - testImplementation("com.augustcellars.cose:cose-java:[1.0.0,2)") - // Transitive dependencies from coreTestOutput testImplementation("org.scala-lang:scala-library:[2.13.1,3)") } diff --git a/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/src/test/java/com/yubico/webauthn/BouncyCastleProviderPresenceTest.java b/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/src/test/java/com/yubico/webauthn/BouncyCastleProviderPresenceTest.java index e38997392..b789838d2 100644 --- a/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/src/test/java/com/yubico/webauthn/BouncyCastleProviderPresenceTest.java +++ b/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/src/test/java/com/yubico/webauthn/BouncyCastleProviderPresenceTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.assertTrue; -import COSE.CoseException; import com.yubico.webauthn.data.AttestationObject; import com.yubico.webauthn.data.RelyingPartyIdentity; import java.io.IOException; @@ -72,7 +71,7 @@ public void bouncyCastleProviderIsNotLoadedAfterInstantiatingRelyingParty() { @Test public void bouncyCastleProviderIsNotLoadedAfterAttemptingToLoadEddsaKey() - throws IOException, CoseException, InvalidKeySpecException { + throws IOException, InvalidKeySpecException { try { WebAuthnCodecs.importCosePublicKey( new AttestationObject( @@ -92,7 +91,7 @@ public void bouncyCastleProviderIsNotLoadedAfterAttemptingToLoadEddsaKey() @Test(expected = NoSuchAlgorithmException.class) public void doesNotFallBackToBouncyCastleAutomatically() - throws IOException, CoseException, InvalidKeySpecException, NoSuchAlgorithmException { + throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { for (Provider prov : Security.getProviders()) { Security.removeProvider(prov.getName()); } diff --git a/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/src/test/java/com/yubico/webauthn/CryptoAlgorithmsTest.java b/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/src/test/java/com/yubico/webauthn/CryptoAlgorithmsTest.java index 78201c6a2..57cd1bcc3 100644 --- a/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/src/test/java/com/yubico/webauthn/CryptoAlgorithmsTest.java +++ b/test-dependent-projects/java-dep-webauthn-server-core-and-bouncycastle/src/test/java/com/yubico/webauthn/CryptoAlgorithmsTest.java @@ -3,7 +3,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import COSE.CoseException; import com.yubico.webauthn.data.AttestationObject; import com.yubico.webauthn.data.RelyingPartyIdentity; import java.io.IOException; @@ -47,7 +46,7 @@ public void tearDown() { @Test public void importRsa() - throws IOException, CoseException, NoSuchAlgorithmException, InvalidKeySpecException { + throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { PublicKey key = WebAuthnCodecs.importCosePublicKey( new AttestationObject( @@ -61,7 +60,7 @@ public void importRsa() @Test public void importEcdsa() - throws IOException, CoseException, NoSuchAlgorithmException, InvalidKeySpecException { + throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { PublicKey key = WebAuthnCodecs.importCosePublicKey( new AttestationObject( @@ -75,7 +74,7 @@ public void importEcdsa() @Test public void importEddsa() - throws IOException, CoseException, NoSuchAlgorithmException, InvalidKeySpecException { + throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { PublicKey key = WebAuthnCodecs.importCosePublicKey( new AttestationObject( diff --git a/test-dependent-projects/java-dep-webauthn-server-core/build.gradle.kts b/test-dependent-projects/java-dep-webauthn-server-core/build.gradle.kts index 1e8977835..29f2ab537 100644 --- a/test-dependent-projects/java-dep-webauthn-server-core/build.gradle.kts +++ b/test-dependent-projects/java-dep-webauthn-server-core/build.gradle.kts @@ -11,9 +11,6 @@ dependencies { testImplementation("junit:junit:4.12") testImplementation("org.mockito:mockito-core:[2.27.0,3)") - // Runtime-only internal dependency of webauthn-server-core - testImplementation("com.augustcellars.cose:cose-java:[1.0.0,2)") - // Transitive dependencies from coreTestOutput testImplementation("org.scala-lang:scala-library:[2.13.1,3)") } diff --git a/test-dependent-projects/java-dep-webauthn-server-core/src/test/java/com/yubico/webauthn/BouncyCastleProviderPresenceTest.java b/test-dependent-projects/java-dep-webauthn-server-core/src/test/java/com/yubico/webauthn/BouncyCastleProviderPresenceTest.java index 6ce756bbc..27c43e834 100644 --- a/test-dependent-projects/java-dep-webauthn-server-core/src/test/java/com/yubico/webauthn/BouncyCastleProviderPresenceTest.java +++ b/test-dependent-projects/java-dep-webauthn-server-core/src/test/java/com/yubico/webauthn/BouncyCastleProviderPresenceTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.assertTrue; -import COSE.CoseException; import com.yubico.webauthn.data.AttestationObject; import com.yubico.webauthn.data.RelyingPartyIdentity; import java.io.IOException; @@ -51,7 +50,7 @@ public void bouncyCastleProviderIsNotLoadedAfterInstantiatingRelyingParty() { @Test public void bouncyCastleProviderIsNotLoadedAfterAttemptingToLoadEddsaKey() - throws IOException, CoseException, InvalidKeySpecException { + throws IOException, InvalidKeySpecException { try { WebAuthnCodecs.importCosePublicKey( new AttestationObject( diff --git a/test-dependent-projects/java-dep-webauthn-server-core/src/test/java/com/yubico/webauthn/CryptoAlgorithmsTest.java b/test-dependent-projects/java-dep-webauthn-server-core/src/test/java/com/yubico/webauthn/CryptoAlgorithmsTest.java index f35ce43ae..5a96dac81 100644 --- a/test-dependent-projects/java-dep-webauthn-server-core/src/test/java/com/yubico/webauthn/CryptoAlgorithmsTest.java +++ b/test-dependent-projects/java-dep-webauthn-server-core/src/test/java/com/yubico/webauthn/CryptoAlgorithmsTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.assertEquals; -import COSE.CoseException; import com.yubico.webauthn.data.AttestationObject; import com.yubico.webauthn.data.RelyingPartyIdentity; import java.io.IOException; @@ -45,7 +44,7 @@ public void tearDown() { @Test public void importRsa() - throws IOException, CoseException, NoSuchAlgorithmException, InvalidKeySpecException { + throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { PublicKey key = WebAuthnCodecs.importCosePublicKey( new AttestationObject( @@ -59,7 +58,7 @@ public void importRsa() @Test public void importEcdsa() - throws IOException, CoseException, NoSuchAlgorithmException, InvalidKeySpecException { + throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { PublicKey key = WebAuthnCodecs.importCosePublicKey( new AttestationObject( diff --git a/webauthn-server-attestation/README.adoc b/webauthn-server-attestation/README.adoc index bd6df8ed4..d591e3cec 100644 --- a/webauthn-server-attestation/README.adoc +++ b/webauthn-server-attestation/README.adoc @@ -21,7 +21,7 @@ This module does four things: - Re-download the metadata BLOB when out of date or invalid. - Provide utilities for selecting trusted metadata entries and authenticators. - Integrate with the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class in the base library, to provide trust root certificates for verifying attestation statements during credential registrations. @@ -30,18 +30,18 @@ Notable *non-features* include: - *Scheduled BLOB downloads.* + The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] class will attempt to download a new BLOB only when its -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] or -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] method is executed. As the names suggest, `loadCachedBlob()` downloads a new BLOB only if the cache is empty or the cached BLOB is invalid or out of date, while `refreshBlob()` always downloads a new BLOB and falls back to the cached BLOB only when the new BLOB is invalid in some way. -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] will never re-download a new BLOB once instantiated. + You should use some external scheduling mechanism to re-run `loadCachedBlob()` @@ -54,12 +54,12 @@ classes keep no internal mutable state. + The FIDO Metadata Service may from time to time report security issues with particular authenticator models. The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] class can be configured with a filter for which authenticators to trust, and untrusted authenticators can be rejected during registration by setting -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] on -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`], but this will not affect any credentials already registered. @@ -94,7 +94,7 @@ Maven: com.yubico webauthn-server-attestation - 2.5.0 + 2.5.1 compile ---------- @@ -102,7 +102,7 @@ Maven: Gradle: ---------- -implementation("com.yubico:webauthn-server-attestation:2.5.0") +implementation("com.yubico:webauthn-server-attestation:2.5.1") ---------- @@ -111,7 +111,7 @@ implementation("com.yubico:webauthn-server-attestation:2.5.0") This library uses link:https://semver.org/[semantic versioning]. The public API consists of all public classes, methods and fields in the `com.yubico.fido.metadata` package and its subpackages, i.e., everything covered by the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/package-summary.html[Javadoc]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/package-summary.html[Javadoc]. Package-private classes and methods are NOT part of the public API. The `com.yubico:yubico-util` module is NOT part of the public API. @@ -123,23 +123,23 @@ Breaking changes to these will NOT be reflected in version numbers. Using this module consists of 5 major steps: 1. Create a - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] instance to download and cache metadata BLOBs, and a - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] instance to make use of the downloaded BLOB. See the JavaDoc for these classes for details on how to construct them. + [WARNING] ===== Unlike other classes in this module and the core library, -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] is NOT THREAD SAFE since its -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] methods read and write caches. -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`], on the other hand, is thread safe, and `FidoMetadataDownloader` instances can be reused for subsequent `loadCachedBlob()` and `refreshBlob()` calls @@ -164,18 +164,18 @@ FidoMetadataService mds = FidoMetadataService.builder() ---------- 2. Set the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] as the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] on your - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] instance, and set - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationConveyancePreference(com.yubico.webauthn.data.AttestationConveyancePreference)[`attestationConveyancePreference(AttestationConveyancePreference.DIRECT)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationConveyancePreference(com.yubico.webauthn.data.AttestationConveyancePreference)[`attestationConveyancePreference(AttestationConveyancePreference.DIRECT)`] on `RelyingParty` to request an attestation statement for new registrations. Optionally also set - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] on `RelyingParty` to require trusted attestation for new registrations. + [source,java] @@ -190,9 +190,9 @@ RelyingParty rp = RelyingParty.builder() ---------- 3. After performing registrations, inspect the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegistrationResult.html#isAttestationTrusted()[`isAttestationTrusted()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegistrationResult.html#isAttestationTrusted()[`isAttestationTrusted()`] result in - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`] to determine whether the authenticator presented an attestation statement that could be verified by any of the trusted attestation certificates in the FIDO Metadata Service. + @@ -209,7 +209,7 @@ if (result.isAttestationTrusted()) { ---------- 4. If needed, use the `findEntries` methods of - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] to retrieve additional authenticator metadata for new registrations. + [source,java] @@ -235,19 +235,19 @@ This step may not be necessary if you use a different provider for the `PKIX` ce == Selecting trusted authenticators The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] class can be configured with filters for which authenticators to trust. When the `FidoMetadataService` is used as the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)[`attestationTrustSource`] in -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`], +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`], this will be reflected in the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegistrationResult.html#isAttestationTrusted()[`.isAttestationTrusted()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegistrationResult.html#isAttestationTrusted()[`.isAttestationTrusted()`] result in -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RegistrationResult.html[`RegistrationResult`]. Any authenticators not trusted will also be rejected for new registrations if you set -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`] on `RelyingParty`. The filter has two stages: a "prefilter" which selects metadata entries to include in the data source, @@ -310,17 +310,17 @@ entry, and the default registration-time filter excludes any authenticator with a matching `ATTESTATION_KEY_COMPROMISE` status report entry. To customize the filters, configure the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.FidoMetadataServiceBuilder.html#prefilter(java.util.function.Predicate)[`.prefilter(Predicate)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.FidoMetadataServiceBuilder.html#prefilter(java.util.function.Predicate)[`.prefilter(Predicate)`] and -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.FidoMetadataServiceBuilder.html#filter(java.util.function.Predicate)[`.filter(Predicate)`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.FidoMetadataServiceBuilder.html#filter(java.util.function.Predicate)[`.filter(Predicate)`] settings in -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`]. The filters are predicate functions; each metadata entry will be included in the data source if and only if the prefilter predicate returns `true` for that entry. Similarly during registration or metadata lookup, the authenticator will be matched with each metadata entry only if the registration-time filter returns `true` for that pair of authenticator and metadata entry. You can also use the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.Filters.html#allOf(java.util.function.Predicate\...)[`FidoMetadataService.Filters.allOf()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.Filters.html#allOf(java.util.function.Predicate\...)[`FidoMetadataService.Filters.allOf()`] combinator to merge several predicates into one. [NOTE] @@ -330,10 +330,10 @@ This is true for both the prefilter and the registration-time filter. If you want to maintain the default filter in addition to the new behaviour, you must include the default condition in the new filter. For example, you can use -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.Filters.html#allOf(java.util.function.Predicate\...)[`FidoMetadataService.Filters.allOf()`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.Filters.html#allOf(java.util.function.Predicate\...)[`FidoMetadataService.Filters.allOf()`] to combine a predefined filter with a custom one. The default filters are available via static functions in -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.Filters.html[`FidoMetadataService.Filters`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.Filters.html[`FidoMetadataService.Filters`]. ===== @@ -354,9 +354,9 @@ This is why any enforceable attestation policy must disallow unknown trust roots Note that unknown and untrusted attestation is allowed by default, but can be disallowed by explicitly configuring -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] with -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`]. +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.RelyingPartyBuilder.html#allowUntrustedAttestation(boolean)[`.allowUntrustedAttestation(false)`]. == Alignment with FIDO MDS spec @@ -366,17 +366,17 @@ link:https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.h The library implements these as closely as possible, but with some slight departures from the spec: * Processing rules steps 1-7 are implemented as specified, by the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] class. All "SHOULD" clauses are also respected, with some caveats: ** Step 3 states "The `nextUpdate` field of the Metadata BLOB specifies a date when the download SHOULD occur at latest". `FidoMetadataDownloader` does not automatically re-download the BLOB. Instead, each time the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#loadCachedBlob()[`loadCachedBlob()`] method is executed it checks whether a new BLOB should be downloaded. The - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html#refreshBlob()[`refreshBlob()`] method always attempts to download a new BLOB when executed, but also does not trigger re-downloads automatically. + @@ -388,7 +388,7 @@ until the next execution of `.loadCachedBlob()` or `.refreshBlob()`. * Metadata entries are not stored or cached individually, instead the BLOB is cached as a whole. In processing rules step 8, neither `FidoMetadataDownloader` nor - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] performs any comparison between versions of a metadata entry. Policy for ignoring metadata entries can be configured via the filter settings in `FidoMetadataService`. See above for details. @@ -400,7 +400,7 @@ There are also some other requirements throughout the spec, which may not be obv states that "The Relying party MUST reject the Metadata Statement if the `authenticatorVersion` has not increased" in an `UPDATE_AVAILABLE` status report. Thus, - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] silently ignores any `MetadataBLOBPayloadEntry` whose `metadataStatement.authenticatorVersion` is present and not greater than or equal to the `authenticatorVersion` in the respective status report. @@ -410,16 +410,16 @@ There are also some other requirements throughout the spec, which may not be obv link:https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#info-statuses[AuthenticatorStatus section] states that "FIDO Servers MUST silently ignore all unknown AuthenticatorStatus values". Thus any unknown status values will be parsed as - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/AuthenticatorStatus.html#UNKNOWN[`AuthenticatorStatus.UNKNOWN`], + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/AuthenticatorStatus.html#UNKNOWN[`AuthenticatorStatus.UNKNOWN`], and - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/MetadataBLOBPayloadEntry.html[`MetadataBLOBPayloadEntry`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/MetadataBLOBPayloadEntry.html[`MetadataBLOBPayloadEntry`] will silently ignore any status report with that status. == Overriding certificate path validation The -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataDownloader.html[`FidoMetadataDownloader`] class uses `CertPathValidator.getInstance("PKIX")` to retrieve a `CertPathValidator` instance. If you need to override any aspect of certificate path validation, such as CRL retrieval or OCSP, you may provide a custom `CertPathValidator` provider for the `"PKIX"` algorithm. diff --git a/webauthn-server-attestation/build.gradle.kts b/webauthn-server-attestation/build.gradle.kts index 9535ce0f1..1748835d2 100644 --- a/webauthn-server-attestation/build.gradle.kts +++ b/webauthn-server-attestation/build.gradle.kts @@ -47,13 +47,8 @@ dependencies { testImplementation("org.scalatest:scalatest_2.13") testImplementation("org.scalatestplus:junit-4-13_2.13") testImplementation("org.scalatestplus:scalacheck-1-16_2.13") - testImplementation("uk.org.lidalia:slf4j-test") - testImplementation("org.slf4j:slf4j-api") { - version { - strictly("[1.7.25,1.8-a)") // Pre-1.8 version required by slf4j-test - } - } + testImplementation("org.slf4j:slf4j-api") } val integrationTest = task("integrationTest") { diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/MetadataBLOBHeader.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/MetadataBLOBHeader.java index 116095dc7..0a4689011 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/MetadataBLOBHeader.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/MetadataBLOBHeader.java @@ -1,5 +1,6 @@ package com.yubico.fido.metadata; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.net.URL; @@ -86,6 +87,9 @@ public Optional getX5u() { * @see RFC 7515 ยง4.1.6. * "x5c" (X.509 Certificate Chain) Header Parameter */ + // @JsonIgnore needed because of: + // https://github.com/FasterXML/jackson-databind/issues/4413#issuecomment-1977989776 + @JsonIgnore public Optional> getX5c() { return Optional.ofNullable(x5c); } diff --git a/webauthn-server-attestation/src/test/resources/slf4jtest.properties b/webauthn-server-attestation/src/test/resources/slf4jtest.properties deleted file mode 100644 index eacb68e5f..000000000 --- a/webauthn-server-attestation/src/test/resources/slf4jtest.properties +++ /dev/null @@ -1 +0,0 @@ -print.level=DEBUG diff --git a/webauthn-server-core/build.gradle.kts b/webauthn-server-core/build.gradle.kts index 79ed2a335..9bbd2bda8 100644 --- a/webauthn-server-core/build.gradle.kts +++ b/webauthn-server-core/build.gradle.kts @@ -16,7 +16,6 @@ dependencies { api(platform(rootProject)) implementation(project(":yubico-util")) - implementation("com.augustcellars.cose:cose-java") implementation("com.fasterxml.jackson.core:jackson-databind") implementation("com.google.guava:guava") implementation("com.upokecenter:cbor") diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/AttestationStatementVerifier.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/AttestationStatementVerifier.java index a962164e3..b815aa320 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/AttestationStatementVerifier.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/AttestationStatementVerifier.java @@ -24,7 +24,6 @@ package com.yubico.webauthn; -import COSE.CoseException; import com.yubico.webauthn.data.AttestationObject; import com.yubico.webauthn.data.AttestationType; import com.yubico.webauthn.data.ByteArray; @@ -34,7 +33,7 @@ interface AttestationStatementVerifier { AttestationType getAttestationType(AttestationObject attestation) - throws IOException, CoseException, CertificateException; + throws IOException, CertificateException; boolean verifyAttestationSignature( AttestationObject attestationObject, ByteArray clientDataJsonHash); diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/FidoU2fAttestationStatementVerifier.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/FidoU2fAttestationStatementVerifier.java index ac3f1f2f1..5806222c2 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/FidoU2fAttestationStatementVerifier.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/FidoU2fAttestationStatementVerifier.java @@ -26,7 +26,6 @@ import static com.yubico.webauthn.Crypto.isP256; -import COSE.CoseException; import com.fasterxml.jackson.databind.JsonNode; import com.yubico.internal.util.ExceptionUtil; import com.yubico.webauthn.data.AttestationObject; @@ -76,7 +75,7 @@ private static boolean validSelfSignature(X509Certificate cert) { } private static ByteArray getRawUserPublicKey(AttestationObject attestationObject) - throws IOException, CoseException { + throws IOException { final ByteArray pubkeyCose = attestationObject .getAuthenticatorData() @@ -102,7 +101,7 @@ private static ByteArray getRawUserPublicKey(AttestationObject attestationObject @Override public AttestationType getAttestationType(AttestationObject attestationObject) - throws CoseException, IOException, CertificateException { + throws IOException, CertificateException { X509Certificate attestationCertificate = getAttestationCertificate(attestationObject); if (attestationCertificate.getPublicKey() instanceof ECPublicKey @@ -153,7 +152,7 @@ && isP256(((ECPublicKey) attestationCertificate.getPublicKey()).getParams()))) { try { userPublicKey = getRawUserPublicKey(attestationObject); - } catch (IOException | CoseException e) { + } catch (IOException e) { RuntimeException err = new RuntimeException( String.format( diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishAssertionSteps.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishAssertionSteps.java index 88b792435..c39a87780 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishAssertionSteps.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishAssertionSteps.java @@ -26,7 +26,6 @@ import static com.yubico.internal.util.ExceptionUtil.assertTrue; -import COSE.CoseException; import com.yubico.internal.util.OptionalUtil; import com.yubico.webauthn.data.AuthenticatorAssertionResponse; import com.yubico.webauthn.data.ByteArray; @@ -495,7 +494,7 @@ public void validate() { try { key = WebAuthnCodecs.importCosePublicKey(cose); - } catch (CoseException | IOException | InvalidKeySpecException e) { + } catch (IOException | InvalidKeySpecException e) { throw new IllegalArgumentException( String.format( "Failed to decode public key: Credential ID: %s COSE: %s", diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishRegistrationSteps.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishRegistrationSteps.java index 172da13dd..4f8e20cd2 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishRegistrationSteps.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishRegistrationSteps.java @@ -27,7 +27,6 @@ import static com.yubico.internal.util.ExceptionUtil.assertTrue; import static com.yubico.internal.util.ExceptionUtil.wrapAndLog; -import COSE.CoseException; import com.upokecenter.cbor.CBORObject; import com.yubico.internal.util.CertificateParser; import com.yubico.internal.util.OptionalUtil; @@ -335,7 +334,7 @@ public void validate() { .collect(Collectors.toList())); try { WebAuthnCodecs.importCosePublicKey(publicKeyCose); - } catch (CoseException | IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { + } catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { throw wrapAndLog(log, "Failed to parse credential public key", e); } } @@ -421,7 +420,7 @@ public AttestationType attestationType() { return AttestationType.UNKNOWN; } } - } catch (IOException | CoseException | CertificateException e) { + } catch (IOException | CertificateException e) { throw new IllegalArgumentException("Failed to resolve attestation type.", e); } } diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/PackedAttestationStatementVerifier.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/PackedAttestationStatementVerifier.java index ed513dbb4..0e8a97bbe 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/PackedAttestationStatementVerifier.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/PackedAttestationStatementVerifier.java @@ -24,7 +24,6 @@ package com.yubico.webauthn; -import COSE.CoseException; import com.fasterxml.jackson.databind.JsonNode; import com.upokecenter.cbor.CBORObject; import com.yubico.internal.util.CertificateParser; @@ -95,7 +94,7 @@ private boolean verifySelfAttestationSignature( .getAttestedCredentialData() .get() .getCredentialPublicKey()); - } catch (IOException | CoseException | InvalidKeySpecException e) { + } catch (IOException | InvalidKeySpecException e) { throw ExceptionUtil.wrapAndLog( log, String.format( diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/TpmAttestationStatementVerifier.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/TpmAttestationStatementVerifier.java index ee051e770..fc91e4205 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/TpmAttestationStatementVerifier.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/TpmAttestationStatementVerifier.java @@ -24,7 +24,6 @@ package com.yubico.webauthn; -import COSE.CoseException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.upokecenter.cbor.CBORObject; @@ -178,7 +177,7 @@ public boolean verifyAttestationSignature( // is identical to the credentialPublicKey in the attestedCredentialData in authenticatorData. try { verifyPublicKeysMatch(attestationObject, pubArea); - } catch (CoseException | IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { + } catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { throw new RuntimeException( "Failed to verify that public key in TPM attestation matches public key in authData.", e); } @@ -267,7 +266,7 @@ private void validateCertInfo( } private void verifyPublicKeysMatch(AttestationObject attestationObject, TpmtPublic pubArea) - throws CoseException, IOException, InvalidKeySpecException, NoSuchAlgorithmException { + throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { final PublicKey credentialPubKey = WebAuthnCodecs.importCosePublicKey( attestationObject diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/WebAuthnCodecs.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/WebAuthnCodecs.java index 94ba42ea1..24dffeb4c 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/WebAuthnCodecs.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/WebAuthnCodecs.java @@ -24,8 +24,6 @@ package com.yubico.webauthn; -import COSE.CoseException; -import COSE.OneKey; import com.google.common.primitives.Bytes; import com.upokecenter.cbor.CBORObject; import com.yubico.webauthn.data.ByteArray; @@ -42,11 +40,39 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.stream.Stream; final class WebAuthnCodecs { - private static final ByteArray ED25519_CURVE_OID = - new ByteArray(new byte[] {0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70}); + private static final ByteArray EC_PUBLIC_KEY_OID = + new ByteArray( + new byte[] { + 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 2, 1 + }); // OID 1.2.840.10045.2.1 ecPublicKey (ANSI X9.62 public key type) + private static final ByteArray P256_CURVE_OID = + new ByteArray( + new byte[] { + 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 3, 1, 7 // OID 1.2.840.10045.3.1.7 + }); + private static final ByteArray P384_CURVE_OID = + new ByteArray(new byte[] {0x2B, (byte) 0x81, 0x04, 0, 34}); // OID 1.3.132.0.34 + private static final ByteArray P512_CURVE_OID = + new ByteArray(new byte[] {0x2B, (byte) 0x81, 0x04, 0, 35}); // OID 1.3.132.0.35 + + private static final ByteArray ED25519_ALG_ID = + new ByteArray( + new byte[] { + // SEQUENCE (5 bytes) + 0x30, + 5, + // OID (3 bytes) + 0x06, + 3, + // OID 1.3.101.112 + 0x2B, + 101, + 112 + }); static ByteArray ecPublicKeyToRaw(ECPublicKey key) { @@ -120,18 +146,15 @@ static ByteArray rawEcKeyToCose(ByteArray key) { } static PublicKey importCosePublicKey(ByteArray key) - throws CoseException, IOException, InvalidKeySpecException, NoSuchAlgorithmException { + throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { CBORObject cose = CBORObject.DecodeFromBytes(key.getBytes()); final int kty = cose.get(CBORObject.FromObject(1)).AsInt32(); switch (kty) { case 1: - // COSE-JAVA is hardcoded to ed25519-java provider ("EdDSA") which would require an - // additional dependency to parse EdDSA keys via the OneKey constructor return importCoseEdDsaPublicKey(cose); case 2: - return importCoseP256PublicKey(cose); + return importCoseEcdsaPublicKey(cose); case 3: - // COSE-JAVA supports RSA in v1.1.0 but not in v1.0.0 return importCoseRsaPublicKey(cose); default: throw new IllegalArgumentException("Unsupported key type: " + kty); @@ -147,8 +170,74 @@ private static PublicKey importCoseRsaPublicKey(CBORObject cose) return KeyFactory.getInstance("RSA").generatePublic(spec); } - private static ECPublicKey importCoseP256PublicKey(CBORObject cose) throws CoseException { - return (ECPublicKey) new OneKey(cose).AsPublicKey(); + private static PublicKey importCoseEcdsaPublicKey(CBORObject cose) + throws NoSuchAlgorithmException, InvalidKeySpecException { + final int crv = cose.get(CBORObject.FromObject(-1)).AsInt32Value(); + final ByteArray x = new ByteArray(cose.get(CBORObject.FromObject(-2)).GetByteString()); + final ByteArray y = new ByteArray(cose.get(CBORObject.FromObject(-3)).GetByteString()); + + final ByteArray curveOid; + switch (crv) { + case 1: + curveOid = P256_CURVE_OID; + break; + + case 2: + curveOid = P384_CURVE_OID; + break; + + case 3: + curveOid = P512_CURVE_OID; + break; + + default: + throw new IllegalArgumentException("Unknown COSE EC2 curve: " + crv); + } + + final ByteArray algId = + encodeDerSequence(encodeDerObjectId(EC_PUBLIC_KEY_OID), encodeDerObjectId(curveOid)); + + final ByteArray rawKey = + encodeDerBitStringWithZeroUnused( + new ByteArray(new byte[] {0x04}) // Raw EC public key with x and y + .concat(x) + .concat(y)); + + final ByteArray x509Key = encodeDerSequence(algId, rawKey); + + KeyFactory kFact = KeyFactory.getInstance("EC"); + return kFact.generatePublic(new X509EncodedKeySpec(x509Key.getBytes())); + } + + private static ByteArray encodeDerLength(final int length) { + if (length <= 127) { + return new ByteArray(new byte[] {(byte) length}); + } else if (length <= 0xffff) { + if (length <= 255) { + return new ByteArray(new byte[] {-127, (byte) length}); + } else { + return new ByteArray(new byte[] {-126, (byte) (length >> 8), (byte) (length & 0x00ff)}); + } + } else { + throw new UnsupportedOperationException("Too long: " + length); + } + } + + private static ByteArray encodeDerObjectId(final ByteArray oid) { + return new ByteArray(new byte[] {0x06, (byte) oid.size()}).concat(oid); + } + + private static ByteArray encodeDerBitStringWithZeroUnused(final ByteArray content) { + return new ByteArray(new byte[] {0x03}) + .concat(encodeDerLength(1 + content.size())) + .concat(new ByteArray(new byte[] {0})) + .concat(content); + } + + private static ByteArray encodeDerSequence(final ByteArray... items) { + final ByteArray content = + Stream.of(items).reduce(ByteArray::concat).orElseGet(() -> new ByteArray(new byte[0])); + return new ByteArray(new byte[] {0x30}).concat(encodeDerLength(content.size())).concat(content); } private static PublicKey importCoseEdDsaPublicKey(CBORObject cose) @@ -166,10 +255,7 @@ private static PublicKey importCoseEd25519PublicKey(CBORObject cose) throws InvalidKeySpecException, NoSuchAlgorithmException { final ByteArray rawKey = new ByteArray(cose.get(CBORObject.FromObject(-2)).GetByteString()); final ByteArray x509Key = - new ByteArray(new byte[] {0x30, (byte) (ED25519_CURVE_OID.size() + 3 + rawKey.size())}) - .concat(ED25519_CURVE_OID) - .concat(new ByteArray(new byte[] {0x03, (byte) (rawKey.size() + 1), 0})) - .concat(rawKey); + encodeDerSequence(ED25519_ALG_ID, encodeDerBitStringWithZeroUnused(rawKey)); KeyFactory kFact = KeyFactory.getInstance("EdDSA"); return kFact.generatePublic(new X509EncodedKeySpec(x509Key.getBytes())); diff --git a/webauthn-server-demo/README b/webauthn-server-demo/README index 6735d1b79..866a97e0b 100644 --- a/webauthn-server-demo/README +++ b/webauthn-server-demo/README @@ -44,7 +44,7 @@ layer. This layer manages the general architecture of the system, and is where most business logic and integration code would go. The demo server implements the "persistent" storage of users and credential registrations - the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] integration point - as the link:src/main/java/demo/webauthn/InMemoryRegistrationStorage.java[`InMemoryRegistrationStorage`] class, which simply keeps them stored in memory for a limited time. The @@ -58,7 +58,7 @@ would be specific to a particular Relying Party (RP) would go in this layer. - The server layer in turn calls the *library layer*, which is where the link:../webauthn-server-core/[`webauthn-server-core`] library gets involved. The entry point into the library is the - link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] + link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/RelyingParty.html[`RelyingParty`] class. + This layer implements the Web Authentication @@ -69,11 +69,11 @@ and exposes integration points for storage of challenges and credentials. Some notable integration points are: + ** The library user must provide an implementation of the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/CredentialRepository.html[`CredentialRepository`] interface to use for looking up stored public keys, user handles and signature counters. ** The library user can optionally provide an instance of the -link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.0/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] +link:https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-core/2.5.1/com/yubico/webauthn/attestation/AttestationTrustSource.html[`AttestationTrustSource`] interface to enable identification and validation of authenticator models. This instance is then used to look up trusted attestation root certificates. The link:../webauthn-server-attestation/[`webauthn-server-attestation`] @@ -158,7 +158,7 @@ correct environment. Authentication demo'` - `YUBICO_WEBAUTHN_USE_FIDO_MDS`: If set to `true` (case-insensitive), use - https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.0/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] + https://developers.yubico.com/java-webauthn-server/JavaDoc/webauthn-server-attestation/2.5.1/com/yubico/fido/metadata/FidoMetadataService.html[`FidoMetadataService`] from the link:../webauthn-server-attestation[`webauthn-server-attestation`] module as a source of attestation data in addition to the static JSON file bundled with the demo. This will write cache files to the