Skip to content

Commit 8aaf483

Browse files
committed
Release 1.0.1
Bugfixes: - Registration no longer fails for unimplemented attestation statement formats if `allowUnknownAttestation` is set to `true`. - Registration still fails for attestation statement formats not defined in the WebAuthn Level 1 spec.
2 parents 42f5a2d + 1c0737f commit 8aaf483

File tree

12 files changed

+190
-96
lines changed

12 files changed

+190
-96
lines changed

.gitmodules

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +0,0 @@
1-
[submodule "lombok"]
2-
path = lombok
3-
url = https://github.com/emlun/lombok.git
4-
branch = builder-javadoc

.travis.yml

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,24 @@ jdk:
1515
script:
1616
- ./gradlew check assembleJavadoc
1717

18-
after_success:
19-
- ./gradlew coveralls
18+
stages:
19+
- test
20+
- mutation-test
21+
- deploy
2022

21-
deploy:
22-
provider: pages
23-
skip-cleanup: true
24-
github-token: $PAGES_DEPLOY_KEY
25-
on:
26-
branch: master
27-
local-dir: 'build/javadoc'
23+
jobs:
24+
include:
25+
- stage: mutation-test
26+
jdk: oraclejdk8
27+
script: ./gradlew pitest coveralls
28+
29+
- stage: deploy
30+
jdk: oraclejdk8
31+
script: ./gradlew assembleJavadoc
32+
deploy:
33+
provider: pages
34+
skip-cleanup: true
35+
github-token: $PAGES_DEPLOY_KEY
36+
on:
37+
branch: master
38+
local-dir: 'build/javadoc'

NEWS

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
== Version 1.0.1 ==
2+
3+
Bugfixes:
4+
5+
* Registration no longer fails for unimplemented attestation statement formats
6+
if `allowUnknownAttestation` is set to `true`.
7+
** Registration still fails for attestation statement formats not defined in
8+
the WebAuthn Level 1 spec.
9+
10+
111
== Version 1.0.0 ==
212

313
* Fixed URL in artifact POM

README

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ Maven:
2727
<dependency>
2828
<groupId>com.yubico</groupId>
2929
<artifactId>webauthn-server-core</artifactId>
30-
<version>1.0.0-RC2</version>
30+
<version>1.0.0</version>
3131
<scope>compile</scope>
3232
</dependency>
3333
----------
3434

3535
Gradle:
3636

3737
----------
38-
compile 'com.yubico:webauthn-server-core:1.0.0-RC2'
38+
compile 'com.yubico:webauthn-server-core:1.0.0'
3939
----------
4040

4141

@@ -60,6 +60,20 @@ and other higher level concepts can make use of this authentication mechanism,
6060
but the authentication mechanism alone does not make a security system.
6161

6262

63+
== Known issues
64+
65+
- In the link:webauthn-server-demo[example app], authentication does not work in
66+
Chrome as of version 70. This is due to a
67+
link:https://bugs.chromium.org/p/chromium/issues/detail?id=847878[bug in
68+
Chrome] which will not be worked around here. To work around this in
69+
application code, you can omit the
70+
link:https://yubico.github.io/java-webauthn-server/webauthn-server-core/com/yubico/webauthn/data/AuthenticatorAssertionResponse.AuthenticatorAssertionResponseBuilder.html#userHandle-java.util.Optional[`userHandle`]
71+
when constructing an
72+
link:https://yubico.github.io/java-webauthn-server/webauthn-server-core/com/yubico/webauthn/data/AuthenticatorAssertionResponse.html[`AuthenticatorAssertionResponse`]
73+
value if the `userHandle` is empty. See
74+
https://github.com/Yubico/java-webauthn-server/issues/12 .
75+
76+
6377
== Documentation
6478

6579
See the
@@ -204,14 +218,14 @@ effects, and does not directly interact with any database. This means it is
204218
database agnostic and thread safe. The following diagram illustrates an example
205219
architecture for an application using the library.
206220

207-
image::https://raw.githubusercontent.com/Yubico/java-webauthn-server/master/docs/img/demo-architecture.svg["Example application architecture",align="center"]
221+
image::https://raw.githubusercontent.com/Yubico/java-webauthn-server/master/docs/img/demo-architecture.svg?sanitize=true["Example application architecture",align="center"]
208222

209223
The application manages all state and database access, and communicates with the
210224
library via POJO representations of requests and responses. The following
211225
diagram illustrates the data flow during a WebAuthn registration or
212226
authentication ceremony.
213227

214-
image::https://raw.githubusercontent.com/Yubico/java-webauthn-server/master/docs/img/demo-sequence-diagram.svg["WebAuthn ceremony sequence diagram",align="center"]
228+
image::https://raw.githubusercontent.com/Yubico/java-webauthn-server/master/docs/img/demo-sequence-diagram.svg?sanitize=true["WebAuthn ceremony sequence diagram",align="center"]
215229

216230
In this diagram, the *Client* is the user's browser and the application's
217231
client-side scripts. The *Server* is the application and its business logic, the

build.gradle

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,8 @@ allprojects {
5151
targetCompatibility = 1.8
5252

5353
lombok {
54-
version '1.18.4'
55-
sha256 = '39f3922deb679b1852af519eb227157ef2dd0a21eec3542c8ce1b45f2df39742'
56-
}
57-
configurations.all {
58-
resolutionStrategy {
59-
dependencySubstitution {
60-
substitute module('org.projectlombok:lombok') with module('com.yubico:lombok:1.18.5-custom')
61-
}
62-
}
54+
version '1.18.6'
55+
sha256 = '6373d9ade79efdc028cd48d40a9af9ac6a090dbcfaec55b438ec49556a4e92fb'
6356
}
6457

6558
tasks.withType(JavaCompile) {
@@ -73,7 +66,6 @@ allprojects {
7366
repositories {
7467
mavenLocal()
7568

76-
maven { url uri("${rootProject.projectDir}/lib") }
7769
maven { url "http://repo.maven.apache.org/maven2" }
7870
}
7971

lib/com/yubico/lombok/1.18.5-custom/README.md

Lines changed: 0 additions & 17 deletions
This file was deleted.
Binary file not shown.

lombok

Lines changed: 0 additions & 1 deletion
This file was deleted.

webauthn-server-core/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jar {
3535
manifest {
3636
attributes([
3737
'Specification-Title': 'Web Authentication: An API for accessing Public Key Credentials',
38-
'Specification-Version': 'Level 1 Candidate Recommendation 2018-03-20',
38+
'Specification-Version': 'Level 1 Proposed Recommendation 2019-01-17',
3939
'Specification-Vendor': 'World Wide Web Consortium',
4040
'Implementation-Id': 'java-webauthn-server',
4141
'Implementation-Title': 'Yubico Web Authentication server library',

webauthn-server-core/src/main/java/com/yubico/webauthn/FinishRegistrationSteps.java

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
import lombok.extern.slf4j.Slf4j;
5454

5555
import static com.yubico.internal.util.ExceptionUtil.assure;
56-
import static com.yubico.webauthn.data.AttestationType.NONE;
5756

5857
@Builder
5958
@Slf4j
@@ -373,24 +372,18 @@ class Step13 implements Step<Step14> {
373372
private final List<String> prevWarnings;
374373

375374
@Override
376-
public void validate() {
377-
assure(formatSupported(), "Unsupported attestation statement format: %s", format());
378-
}
375+
public void validate() {}
379376

380377
@Override
381378
public Step14 nextStep() {
382-
return new Step14(clientDataJsonHash, attestation, attestationStatementVerifier().get(), allWarnings());
379+
return new Step14(clientDataJsonHash, attestation, attestationStatementVerifier(), allWarnings());
383380
}
384381

385382
public String format() {
386383
return attestation.getFormat();
387384
}
388385

389-
public boolean formatSupported() {
390-
return attestationStatementVerifier().isPresent();
391-
}
392-
393-
private Optional<AttestationStatementVerifier> attestationStatementVerifier() {
386+
public Optional<AttestationStatementVerifier> attestationStatementVerifier() {
394387
switch (format()) {
395388
case "fido-u2f":
396389
return Optional.of(new FidoU2fAttestationStatementVerifier());
@@ -410,15 +403,19 @@ private Optional<AttestationStatementVerifier> attestationStatementVerifier() {
410403
class Step14 implements Step<Step15> {
411404
private final ByteArray clientDataJsonHash;
412405
private final AttestationObject attestation;
413-
private final AttestationStatementVerifier attestationStatementVerifier;
406+
private final Optional<AttestationStatementVerifier> attestationStatementVerifier;
414407
private final List<String> prevWarnings;
415408

416409
@Override
417410
public void validate() {
418-
assure(
419-
attestationStatementVerifier.verifyAttestationSignature(attestation, clientDataJsonHash),
420-
"Invalid attestation signature."
421-
);
411+
attestationStatementVerifier.ifPresent(verifier -> {
412+
assure(
413+
verifier.verifyAttestationSignature(attestation, clientDataJsonHash),
414+
"Invalid attestation signature."
415+
);
416+
});
417+
418+
assure(attestationType() != null, "Failed to determine attestation type");
422419
}
423420

424421
@Override
@@ -428,18 +425,40 @@ public Step15 nextStep() {
428425

429426
public AttestationType attestationType() {
430427
try {
431-
return attestationStatementVerifier.getAttestationType(attestation);
428+
if (attestationStatementVerifier.isPresent()) {
429+
return attestationStatementVerifier.get().getAttestationType(attestation);
430+
} else {
431+
switch (attestation.getFormat()) {
432+
case "android-key":
433+
// TODO delete this once android-key attestation verification is implemented
434+
return AttestationType.BASIC;
435+
case "tpm":
436+
// TODO delete this once tpm attestation verification is implemented
437+
if (attestation.getAttestationStatement().has("x5c")) {
438+
return AttestationType.ATTESTATION_CA;
439+
} else {
440+
return AttestationType.ECDAA;
441+
}
442+
default:
443+
throw new IllegalArgumentException("Failed to resolve attestation type; unknown attestation statement format: " + attestation.getFormat());
444+
}
445+
}
432446
} catch (IOException | CoseException | CertificateException e) {
433447
throw new IllegalArgumentException("Failed to resolve attestation type.", e);
434448
}
435449
}
436450

437451
public Optional<List<X509Certificate>> attestationTrustPath() {
438-
if (attestationStatementVerifier instanceof X5cAttestationStatementVerifier) {
439-
try {
440-
return ((X5cAttestationStatementVerifier) attestationStatementVerifier).getAttestationTrustPath(attestation);
441-
} catch (CertificateException e) {
442-
throw new IllegalArgumentException("Failed to resolve attestation trust path.", e);
452+
if (attestationStatementVerifier.isPresent()) {
453+
AttestationStatementVerifier verifier = attestationStatementVerifier.get();
454+
if (verifier instanceof X5cAttestationStatementVerifier) {
455+
try {
456+
return ((X5cAttestationStatementVerifier) verifier).getAttestationTrustPath(attestation);
457+
} catch (CertificateException e) {
458+
throw new IllegalArgumentException("Failed to resolve attestation trust path.", e);
459+
}
460+
} else {
461+
return Optional.empty();
443462
}
444463
} else {
445464
return Optional.empty();
@@ -456,10 +475,6 @@ class Step15 implements Step<Step16> {
456475

457476
@Override
458477
public void validate() {
459-
assure(
460-
attestationType == AttestationType.SELF_ATTESTATION || attestationType == NONE || trustResolver().isPresent(),
461-
"Failed to obtain attestation trust anchors."
462-
);
463478
}
464479

465480
@Override
@@ -504,6 +519,11 @@ class Step16 implements Step<Step17> {
504519

505520
@Override
506521
public void validate() {
522+
assure(
523+
trustResolver.isPresent() || allowUntrustedAttestation,
524+
"Failed to obtain attestation trust anchors."
525+
);
526+
507527
switch (attestationType) {
508528
case SELF_ATTESTATION:
509529
assure(allowUntrustedAttestation, "Self attestation is not allowed.");

0 commit comments

Comments
 (0)