w3c / webauthn

Web Authentication: An API for accessing Public Key Credentials

Home Page:https://w3c.github.io/webauthn/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Make AuthenticatorAttestationResponseJSON.clientDataJSON a DOMString or USVString

zacknewman opened this issue · comments

JSON-compatible serialization must be used for clientDataJSON. This makes the ArrayBuffer a USVString for platforms whose native string encoding is UTF-8. For such platforms, it would be easier and faster to just send the data as is; and for non-UTF-8 platforms, encoding the ArrayBuffer such that it is a USVString/DOMString is better than having to encode it as a Base64URLString which itself is a subset of DOMString anyway. Is the reason AuthenticatorAttestationResponseJSON.clientDataJSON defined that way for consistency alone? Specifically that all ArrayBuffers are converted to that?

On the server side, the data needs to be decoded twice: first to transform the Base64URLString into UTF-8 data and second to transform the UTF-8 data into a JSON map; however if the data were simply a USVString/DOMString, the server would only need to decode the data once as a JSON map.

Is the reason AuthenticatorAttestationResponseJSON.clientDataJSON defined that way for consistency alone? Specifically that all ArrayBuffers are converted to that?

Yes, the intention was to use consistent encoding of ArrayBuffer values to something that would survive transmission to the server. As clientDataJSON is bytes it made sense to treat it like any other ArrayBuffer, even though as is outlined in the spec it's handled as a UTF-8 string during response verification.

Fair enough.

Adding to what @MasterKale said - critically, the encoding of clientDataJSON MUST remain byte-for-byte identical in transmission from authenticator to server so that the signature over it remains valid. Intermediate parties should not be led to believe that they can safely parse and re-serialize the JSON, because any change to it would break the signature even if the re-serialized JSON is semantically equivalent. Therefore clientDataJSON is defined as a byte array rather than a DOMString, to emphasize that intermediate parties should consider it as opaque. These are essentially the same reasons as why JWS also serializes everything as base64-encoded byte arrays even though many of the components are JSON data.

Adding to what @MasterKale said - critically, the encoding of clientDataJSON MUST remain byte-for-byte identical in transmission from authenticator to server so that the signature over it remains valid. Intermediate parties should not be led to believe that they can safely parse and re-serialize the JSON, because any change to it would break the signature even if the re-serialized JSON is semantically equivalent. Therefore clientDataJSON is defined as a byte array rather than a DOMString, to emphasize that intermediate parties should consider it as opaque. These are essentially the same reasons as why JWS also serializes everything as base64-encoded byte arrays even though many of the components are JSON data.

clientDataJSON is not the only item that must remain identical though. For example RP ID must also remain identical since a SHA-256 hash of it is matched with rpIdHash in authData; yet PublicKeyCredentialRpEntity.id is modeled as a DOMString and not ArrayBuffer. I find it more likely RP ID gets altered in transit since an RP ID only needs to be a valid domain string. For example, www.EXample.com very likely will be altered into www.example.com when sent from the server to authenticator which will cause the registration to fail. Perhaps that is an issue with how RP ID is defined; but even if the definition were improved to require the RP ID to be the result of the domain-to-ascii algorithm, it would still be essential it does not get altered and thus be modeled as an ArrayBuffer.