Skip to content

Commit

Permalink
rough WIP to fix issue #1701 side-channel attack
Browse files Browse the repository at this point in the history
  • Loading branch information
equalsJeffH committed Mar 19, 2022
1 parent 2730294 commit f0fe8f2
Showing 1 changed file with 83 additions and 15 deletions.
98 changes: 83 additions & 15 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -6105,7 +6105,10 @@ A usage example is thus:

Issue(w3c/webauthn#1701): TODO: **The below paragraph is presently inaccurate and will be revised or removed as part of the resolution to Issue #1701.** RPs will likely need to verify the device-bound key's signature on a per-request basis.

A [=[RP]=] utilizing this extension will only need to perform thorough verification of the [=device public key=]'s `attObjForDevicePublicKey` once: i.e., the first time a new [=device public key=] is received. "Thorough verification" means verifying the attestation statement `$$attStmtType` (per the [=attestation statement format=]'s "signing procedure", see [[#sctn-generating-an-attestation-object]]) as well as the `sig` value: see [[#sctn-device-publickey-extension-verification]]. Upon successful verification, the [=[RP]=] SHOULD cache the `aaguid`, `dpk`, `scope`, and `$$attStmtType` values. Upon receiving subsequent `devicePubKey` extension output values, the [=[RP]=] can, along with verifying the `sig` value of each extension output occurrence, do binary equality checks of the cached `aaguid`, `dpk`, `scope`, and `$$attStmtType` values against those returned in the extension output.
A [=[RP]=] utilizing this extension will only need to perform thorough verification of the [=device public key=]'s `attObjForDevicePublicKey` once: i.e., the first time a new [=device public key=] is received. "Thorough verification" means verifying the attestation statement `$$attStmtType` (per the [=attestation statement format=]'s "signing procedure", see [[#sctn-generating-an-attestation-object]]) as well as the `sig` value: see [[#sctn-device-publickey-extension-verification]].


Upon successful verification, the [=[RP]=] SHOULD cache the `aaguid`, `dpk`, `scope`, and `$$attStmtType` values. Upon receiving subsequent `devicePubKey` extension output values, the [=[RP]=] can, along with verifying the `sig` value of each extension output occurrence, do binary equality checks of the cached `aaguid`, `dpk`, `scope`, and `$$attStmtType` values against those returned in the extension output. If any of `aaguid`, `dpk`, `scope` do not compare as equal, then the response is invalid. Otherwise, they are all equal, and if `$$attStmtType` does not compare as equal, then the [=[RP]=] should verify the attestation signature. If


### Extension Definition ### {#sctn-device-publickey-extension-definition}
Expand Down Expand Up @@ -6181,14 +6184,20 @@ A [=[RP]=] utilizing this extension will only need to perform thorough verificat
; 0x01 means "per-app" scope.
; Values other than 0x00 or 0x01 are reserved for future use.

; An authenticator-generated random nonce for inclusion in the attestation
; signature. If the authenticator chooses to not generate a nonce, it sets this
; to a zero-length bytestring.

nonce: bstr .size (0..32),

; See https://www.w3.org/TR/webauthn/#sctn-generating-an-attestation-object
;
; Attestation statement formats define the \`fmt\` and \`attStmt\` members of
; $$attStmtType.
;
; In summary, the \`attStmt\` will (typically) contain:
; (1) a SIGNATURE value calculated (using the attestation private key)
; over (aaguid || dpk).
; over (aaguid || dpk || nonce).
; (2) the attestation certificate or public key, and supporting certificates,
; if any.
;
Expand All @@ -6215,21 +6224,27 @@ A [=[RP]=] utilizing this extension will only need to perform thorough verificat

1. Let |clientDataHash| be the [=hash of the serialized client data=].

1. Let |randomNonce| be a fresh randomly-generated bytestring of 32 bytes maximum length, or a zero length bytestring if the authenticator chooses to not generate a nonce.

Note: |randomNonce|'s purpose is to randomize the `devicePubKey` extension's attestation signature value. If this is not done, then the `devicePubKey` extension's attestation signature value remains constant for all such signatures issued on behalf of this user credential, possibly exposing the authenticator's attestation private key to side-channel attacks. The randomness-generation mechanism should be carefully chosen by the authenticator implementer.

1. Let the `devicePubKey` [=authenticator extension output=] value be a [=CBOR=] map as defined by `attObjForDevicePublicKey` above, with keys and values as follows:

1. Let the `aaguid` key's value be |aaguid|.

1. Let the `dpk` key's value be |dpk|.

1. Let the `nonce` key's value be |randomNonce|.

1. Let the `sig` key's value be the result of signing the concatenation of |clientDataHash| and |userCredentialId| using the |devicePrivateKey| and the signature algorithm appropriate for the |devicePrivateKey|'s public key algorithm: <code>sign((|clientDataHash| || |userCredentialId|), |devicePrivateKey|)</code>.

1. Let the `scope` key have the value zero (0x00) if this is an "entire device" [=device public key=]. Otherwise, let `scope` have the value one (0x01), indicating a more narrow per-app scope.

1. Let the values of the `$$attStmtType` "group socket" [[=CDDL=]] be the result of generating an [=attestation statement=] in the [=attestation statement format=] appropriate for this [=authenticator=] (see the Note below), although substituting |aaguid| for `authenticatorData`, and substituting |dpk| for `clientDataHash` in the [=attestation statement format=]'s signing procedure (see [[#sctn-generating-an-attestation-object]]). Attestation statement formats define the `fmt` and `attStmt` members of the `$$attStmtType` group socket.
1. Let the values of the `$$attStmtType` "group socket" [[=CDDL=]] be the result of generating an [=attestation statement=] in the [=attestation statement format=] appropriate for this [=authenticator=] (see the Note below), although substituting |aaguid|'s value for `authenticatorData`, and substituting the concatenation of |dpk|'s value and |nonce|'s value (in that order) for `clientDataHash` in the [=attestation statement format=]'s signing procedure (see [[#sctn-generating-an-attestation-object]]). If |nonce|'s value is a zero length bytestring, then there is no nonce value to concatenate. Attestation statement formats define the `fmt` and `attStmt` members of the `$$attStmtType` group socket.

In summary, the `$$attStmtType` values generated by the foregoing procedure typically contain a signature value calculated over the bytes of <code>(|aaguid| || |dpk|)</code>, the attestation certificate or public key, and supporting certificates, if any.
In summary, the `$$attStmtType` values generated by the foregoing procedure typically contain a signature value calculated over the bytes of <code>(|aaguid| || |dpk| || |nonce|)</code>, the attestation certificate or public key, and supporting certificates, if any.

Note: The details of the `$$attStmtType` values are dependent upon the particular [=attestation statement=] format. See [[#sctn-attestation-formats]]. Since the [=hardware-bound device key pair=] is specific to a particular authenticator, its attestation can be tied to hardware roots of trust. This is in contrast to the associated [=user credential=]'s attestation.
Note: The details of the `$$attStmtType` values are dependent upon the particular [=attestation statement=] format. See [[#sctn-attestation-formats]]. Since the [=hardware-bound device key pair=] is specific to a particular authenticator, its attestation can be tied to hardware roots of trust (although they do not have to be). This is in contrast to the associated [=user credential=]'s attestation.

### `devicePubKey` Extension Output Verification Procedures ### {#sctn-device-publickey-extension-verification}

Expand All @@ -6241,13 +6256,15 @@ If the `devicePubKey` extension was included on a {{CredentialsContainer/create(

1. Let |attObjForDevicePublicKey| be the value of the `devicePubKey` member of |clientExtensionResults|.

1. Verify that |attObjForDevicePublicKey| is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields: |sig|, |aaguid|, |dpk|, |scope|, |fmt|, |attStmt|.
1. Verify that |attObjForDevicePublicKey| is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields: |sig|, |aaguid|, |dpk|, |scope|, |nonce|, |fmt|, |attStmt|.

Note: The latter |attObjForDevicePublicKey| fields are referenced exclusively in the below steps and are not to be confused with other fields with the same names in other portions of [=authenticatorData=].

1. Verify that |sig| is a valid signature over the concatenation of |hash| and <code>[=credentialId=]</code> using the [=device public key=] |dpk| (the signature algorithm is indicated by |dpk|'s "alg" {{COSEAlgorithmIdentifier}} value).

1. Verify that |attObjForDevicePublicKey|.|attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |attObjForDevicePublicKey|.|fmt|'s [=verification procedure=] given |attObjForDevicePublicKey|.|attStmt|, although substituting |attObjForDevicePublicKey|.|aaguid| for `authenticatorData`, and substituting |dpk| for `clientDataHash` in the [=attestation statement format=]'s [=verification procedure inputs=].
1. Verify that |attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |fmt|'s [=verification procedure=] given |attStmt|, although substituting |aaguid|'s value for `authenticatorData`, and substituting the concatenation of |dpk|'s value and |nonce|'s value for `clientDataHash` in the [=attestation statement format=]'s [=verification procedure inputs=].

Note: If |attObjForDevicePublicKey|.|fmt|'s value is "[=none=]", there is no attestation signature to verify.
Note: If |fmt|'s value is "[=none=]" there is no attestation signature to verify.

1. Store the |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=].

Expand All @@ -6260,23 +6277,74 @@ If the `devicePubKey` extension was included on a {{CredentialsContainer/get()|n

1. Let |attObjForDevicePublicKey| be the value of the `devicePubKey` member of |clientExtensionResults|.

1. Verify that |attObjForDevicePublicKey| is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields: |sig|, |aaguid|, |dpk|, |scope|, |fmt|, |attStmt|.
1. Verify that |attObjForDevicePublicKey| is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields: |sig|, |aaguid|, |dpk|, |scope|, |nonce|, |fmt|, |attStmt|.

Note: The latter |attObjForDevicePublicKey| fields are referenced exclusively in the below steps and are not to be confused with other fields with the same names in other portions of [=authenticatorData=].

1. Verify that |sig| is a valid signature over the concatenation of |hash| and <code>[=credentialId=]</code> using the [=device public key=] |dpk| (the signature algorithm is indicated by |dpk|'s "alg" {{COSEAlgorithmIdentifier}} value).

1. If the [=[RP]=]'s [=user account|account=] mapped to the <code>|credential|.{{Credential/id}}</code> in play (i.e., for the user being authenticated) holds values corresponding to the extracted |attObjForDevicePublicKey| fields: perform binary equality checks between the corresponding stored and extracted values, then if the results are:
1. If the [=[RP]=]'s [=user account=] mapped to the <code>|credential|.{{Credential/id}}</code> in play (i.e., for the user being authenticated) holds |aaguid|, |dpk|, |scope|, |fmt|, and |attStmt| values corresponding to the extracted |attObjForDevicePublicKey| fields, then perform binary equality checks between the corresponding stored {|aaguid|, |dpk|, |scope|, |fmt|} values and the extracted |aaguid|, |dpk|, |scope|, |fmt| values. The [=[RP]=] may have more than one set of {|aaguid|, |dpk|, |scope|, |fmt|, |attStmt|} values mapped to the [=user account=] and each set must be checked until one matches.

If the results are:

<dl class="switch">
: successful
:: This is a known [=device public key=] and thus a known device. Terminate these verification steps.
:: This is likely a known device:

If |fmt|'s value is "none" then there is no attestation signature to verify and this is a known [=device public key=] with a valid |sig| and thus a known device. Terminate these verification steps.

Otherwise, check |attObjForDevicePublicKey|'s |attStmt| by performing a binary equality check between the corresponding stored and extracted |attStmt| values. If the result is:
<dl class="switch">
: successful
:: This is a known [=device public key=] with a valid |sig| and valid attestation and thus a known device. Terminate these verification steps.

Note: This authenticator is not generating a fresh per-response random nonce.

: unsuccessful
:: Verify that |attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |fmt|'s [=verification procedure=] given |attStmt|, although substituting |aaguid|'s value for `authenticatorData`, and substituting the concatenation of |dpk|'s value and |nonce|'s value for `clientDataHash` in the [=attestation statement format=]'s [=verification procedure inputs=].

If the result is:

<dl class="switch">
: successful
:: This is a known [=device public key=] with a valid |sig| and valid attestation and thus a known device. Terminate these verification steps.

Note: This authenticator is generating a fresh per-response random nonce.

: unsuccessful
:: Some form of error has occurred. It is indeterminate whether this is a known device. Terminate these verification steps.
</dl>
</dl>

: unsuccessful
:: This is a new [=device public key=] signifying a new device:
1. Verify that |attObjForDevicePublicKey|.|attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |attObjForDevicePublicKey|.|fmt|'s [=verification procedure=] given |attObjForDevicePublicKey|.|attStmt|, although substituting |attObjForDevicePublicKey|.|aaguid| for `authenticatorData`, and substituting |dpk| for `clientDataHash` in the [=attestation statement format=]'s [=verification procedure inputs=].
1. Store the |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=]. Terminate these verification steps.
:: This is possibly a new [=device public key=] signifying a new device:

1. If |attObjForDevicePublicKey|.|dpk| did not match any of the [=[RP]=]'s stored |dpk| values for this [=user account=] and <code>|credential|.{{Credential/id}}</code> pair then:

<dl class="switch">
: If |fmt|'s value is "none" then there is no attestation signature to verify and this is a new device.
:: Store the |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=]. Terminate these verification steps.

: Otherwise:
:: Verify that |attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |fmt|'s [=verification procedure=] given |attStmt|, although substituting |aaguid|'s value for `authenticatorData`, and substituting the concatenation of |dpk|'s value and |nonce|'s value for `clientDataHash` in the [=attestation statement format=]'s [=verification procedure inputs=].

If the result is:

<dl class="switch">
: successful
:: This is a new [=device public key=] signifying a new device. Store the |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=]. Terminate these verification steps.

: unsuccessful
:: Some form of error has occurred. It is indeterminate whether this is a valid new device. Terminate these verification steps.
</dl>
</dl>
1. Otherwise something weird is going on because we recieved a known |dpk| value, but one or more of the accompanying |aaguid|, |scope|, or |fmt| values did not match what the [=[RP]=] as stored along with that |dpk| value.
</dl>

1. Otherwise, |attObjForDevicePublicKey| fields are not presently mapped to this [=user account=] and <code>|credential|.{{Credential/id}}</code> pair. Verify the |attObjForDevicePublicKey|.|attStmt| as given above, and if successful then store the |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=].
1. Otherwise, the [=[RP]=] does not have |attObjForDevicePublicKey| fields presently mapped to this [=user account=] and <code>|credential|.{{Credential/id}}</code> pair,

1. If |fmt|'s value is "none" then there is no attestation signature to verify. Otherwise, verify the |attStmt| as given above.
1. If successful then store the |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=].

See also [[#sctn-device-publickey-extension-usage]] for further details.

Expand Down

0 comments on commit f0fe8f2

Please sign in to comment.