w3c / webauthn

Web Authentication: An API for accessing Public Key Credentials
https://w3c.github.io/webauthn/
Other
1.19k stars 172 forks source link

Compound attestation statement format is incompatible with attStmtTemplate #2210

Open emlun opened 1 day ago

emlun commented 1 day ago

6.5.4. Generating an Attestation Object defines a CDDL template for attestation statement formats:

attObj = {
            authData: bytes,
            $$attStmtType
         }

attStmtTemplate = (
                      fmt: text,
                      attStmt: { * tstr => any } ; Map is filled in by each concrete attStmtType
                  )

; Every attestation statement format must have the above fields
attStmtTemplate .within $$attStmtType

Note that the attStmt member is defined as a CBOR map.

§8.9. Compound Attestation Statement Format defines a choice for the $$attStmtType group socket:

$$attStmtType //= (
                      fmt: "compound",
                      attStmt: [2* nonCompoundAttStmt]
                  )

nonCompoundAttStmt = { $$attStmtType } .within { fmt: text .ne "compound" }

Note that the attStmt member here is a CBOR array.

I also don't think the expression attStmtTemplate .within $$attStmtType successfully encodes the intent Every attestation statement format must have the above fields, for two reasons: it does not define a CDDL rule since it contains no = sign, and even if it did, the .within control operator would apply only to the new type defined by that rule, but not to the attObj type.

On the 2024-11-20 WG call it was said that there are implementations of compound attestation shipping, so the preferred resolution to this is to relax the template to allow array-based attestation statements.

Proposed Change

  1. Inline the .within control operator into the attObj definition:

    attObj = {
        authData: bytes,
        $$attStmtType
    } .within attStmtTemplate  ; Every attestation statement format must have the fields below
  2. Add a choice to attStmtTemplate to allow an array for attStmt:

    attStmtTemplate = {
        authData: bytes,
        fmt: text,
        attStmt: (
            { * tstr => any } ; Map is filled in by each concrete attStmtType
            //
            [ * any ]         ; attStmt may also be an array
        ),
    }