Yubico / java-webauthn-server

Server-side Web Authentication library for Java https://www.w3.org/TR/webauthn/#rp-operations
Other
443 stars 139 forks source link

`AuthenticatorAttestationResponse` JSON contains additional property `attestation` #296

Closed jbb01 closed 1 year ago

jbb01 commented 1 year ago

When serializing an AuthenticatorAttestationResponse with Jackson, an additional property attestation is present. This causes an UnrecognizedPropertyException when trying to deserialize that json back into an AuthenticatorAttestationResponse. The problem seems to be caused by a missing @JsonIgnore on the getter AuthenticatorAttestationResponse::getAttestation().

The following code

AuthenticatorAttestationResponse response = /* ... */;
ObjectMapper objectMapper = new ObjectMapper();
String responseJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(response);
System.out.println(responseJson);

objectMapper.readValue(responseJson, AuthenticatorAttestationResponse.class);

first prints

{
  "attestationObject" : "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZzkBAGNzaWdAaGF1dGhEYXRhWFIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAKQBAwM5AQAgQCFA",
  "clientDataJSON" : "eyJjaGFsbGVuZ2UiOiAiQUFBQUFBQUFBQUFBQUFBQUFBQUFBQSIsICJvcmlnaW4iOiAiZXhhbXBsZS5jb20iLCAidHlwZSI6ICJ3ZWJhdXRobi5jcmVhdGUiLCAiY3Jvc3NPcmlnaW4iOiBmYWxzZX0",
  "transports" : [ ],
  "attestation" : "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZzkBAGNzaWdAaGF1dGhEYXRhWFIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAKQBAwM5AQAgQCFA"
}

and then throws

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "attestation" (class com.yubico.webauthn.data.AuthenticatorAttestationResponse), not marked as ignorable (3 known properties: "transports", "clientDataJSON", "attestationObject"])
 at [Source: (String)"{"attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZzkBAGNzaWdAaGF1dGhEYXRhWFIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAKQBAwM5AQAgQCFA","clientDataJSON":"eyJjaGFsbGVuZ2UiOiAiQUFBQUFBQUFBQUFBQUFBQUFBQUFBQSIsICJvcmlnaW4iOiAiZXhhbXBsZS5jb20iLCAidHlwZSI6ICJ3ZWJhdXRobi5jcmVhdGUiLCAiY3Jvc3NPcmlnaW4iOiBmYWxzZX0","transports":[],"attestation":"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZzkBAGNzaWdAaGF1dGhEYXRhWFIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEAAA"[truncated 64 chars]; line: 1, column: 395] (through reference chain: com.yubico.webauthn.data.AuthenticatorAttestationResponse["attestation"])
        at com.fasterxml.jackson.databind@2.15.0/com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
    at com.fasterxml.jackson.databind@2.15.0/com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:1138)
    at com.fasterxml.jackson.databind@2.15.0/com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:2224)
    at com.fasterxml.jackson.databind@2.15.0/com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1709)
    at com.fasterxml.jackson.databind@2.15.0/com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1687)
    at com.fasterxml.jackson.databind@2.15.0/com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:284)

This has been tested with com.yubico:webauthn-server-core:2.4.1, com.fasterxml.jackson.core:jackson-core:2.15.0 and Temurin-17.0.6+10. The AuthenticatorAttestationResponse object was created by the following code

byte[] credentialId = new byte[16];

CBORObject cborPublicKeyCose = CBORObject.NewOrderedMap();
cborPublicKeyCose.set(1, CBORObject.FromObject(3));
cborPublicKeyCose.set(3, CBORObject.FromObject(-257));
cborPublicKeyCose.set(-1, CBORObject.FromObject(new byte[0])); // RSA modulus
cborPublicKeyCose.set(-2, CBORObject.FromObject(new byte[0])); // RSA exponent
byte[] publicKeyCose = cborPublicKeyCose.EncodeToBytes();

ByteBuffer authenticatorDataBuffer = ByteBuffer.wrap(new byte[32 + 1 + 4 + 16 + 2 + credentialId.length + publicKeyCose.length]);
authenticatorDataBuffer.put(new byte[32]); // rpIdHash
authenticatorDataBuffer.put((byte) 0b01000001); // flags: UP & AT
authenticatorDataBuffer.putInt(0); // signCount
authenticatorDataBuffer.put(new byte[16]); // AAGUID
authenticatorDataBuffer.putShort((short) credentialId.length);
authenticatorDataBuffer.put(credentialId);
authenticatorDataBuffer.put(publicKeyCose);
byte[] authenticatorData = authenticatorDataBuffer.array();

CBORObject attStmt = CBORObject.NewOrderedMap();
attStmt.set("alg", CBORObject.FromObject(-257));
attStmt.set("sig", CBORObject.FromObject(new byte[0]));

CBORObject attestationObject = CBORObject.NewOrderedMap();
attestationObject.set("fmt", CBORObject.FromObject("packed"));
attestationObject.set("attStmt", attStmt);
attestationObject.set("authData", CBORObject.FromObject(authenticatorData));

String clientDataJSON = String.format(
        "{\"challenge\": \"%s\", \"origin\": \"example.com\", \"type\": \"webauthn.create\", \"crossOrigin\": false}",
        new ByteArray(new byte[16]).getBase64Url() // challenge
);

AuthenticatorAttestationResponse response = AuthenticatorAttestationResponse.builder()
        .attestationObject(new ByteArray(attestationObject.EncodeToBytes()))
        .clientDataJSON(new ByteArray(clientDataJSON.getBytes(StandardCharsets.UTF_8)))
        .build();
emlun commented 1 year ago

Thanks for the report! For most normal use of the library this was fixed by commit 5d1effca2b3ce9312ec23c4e8b4b2acbfdad25d1 in release 2.4.1 (see also the Jackson issue referenced in the diff), but I realize now that this fix only applies to the ObjectMapper used internally by the library. I suppose the @JsonIgnore annotation is necessary after all. This will be fixed in the next release.

jbb01 commented 1 year ago

Thanks for your fast response. The commit you linked helped in fixing my problem.

emlun commented 1 year ago

Fixed and will be released in version 2.5.0. Thanks again for the report!