Skip to content

Windows 11 Hello TPM attestation fails with invalid_pub_area (ECC pubArea parsed with extra length) #52

@makmn1

Description

@makmn1

Summary

On Windows 11 Hello (platform authenticator) with direct attestation (fmt: "tpm"), Wax raises:

(Wax.AttestationVerificationError) Failed to verify attestation of type tpm (reason: invalid_pub_area)

Root cause: in the ECC branch of parse_pub_area/1, the code reads an extra 2-byte length before the ECC point coordinates (x, y). That extra length does not exist in the TPM’s TPMT_PUBLIC encoding for ECC, so valid pubArea values (like Windows Hello’s) fail to parse. You can verify this with the pubArea key I got back in the parsed response below.

Parsed attestationObject (x5c data for brevity)
{
  "fmt": "tpm",
  "attStmt": {
    "alg": "RS1 (-65535)",
    "sig": "KZdEuRgthOo7pRTE0k4IwZB76_lIEi0hrel8ifLazDC7caHzm7tXeErovFhbuF086F33zlOzyneI9x74SK27pXBciwDImXq_-FfU5yIYhkYNHVVk7KHfNYGlN_xqbWbnCvy-Jt66RBSMNTCOfqmG62SwyzjP4Gc-ckoSv1kzJTR1FTK_-pUvGrnZSqVys1nWyUpPG5_UXqdOyLRImHwaASbKu26Ce4zkWfp1v7lKo-5Wh0PhihZCxWqsi2ZW4S2v-SgpdiYFgRQHmfblK9sHJbYoPJ3VsCkDxKZwRuWHSn1GklyBZe1V0vyX4FJgMQF4yS6tnSfjsHAecK2YQlMjTw",
    "x5c": [],
    "ver": "2.0",
    "certInfo": "_1RDR4AXACIAC86eZELTxXtiaYmxvCMg1tnI_xPpQRccdPWPy_GD35XlABQQQTmX2bXpT6ZbF81SI5KvwYbDCAAAAAAx7b2KLGE1DWdjq-8BikYVrwyQfwQAIgALBecDy19UewMVshMFfdDIQoSfm3XuSXDGRMBXosm1OqoAIgALQ9haTPfwkEvFJLFi3rA2QKDaG21N4D4ce3aGfveWmeo",
    "pubArea": "ACMACwAEAHIAIJ3_y_NsODrmmfuYaNxty4nXFTiEvigDkiwSQVi_rSKuABAAEAADABAAIB1OjbHkSpSgqpLtt9WvqRYQVtyAk7ke-hwPZ_LpPYbsACB4u-L3TI-64HtHRbz0upEldzwTrq6Th7AAII-0UizA0g"
  },
  "authData": {
    "rpIdHash": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M",
    "flags": {
      "userPresent": true,
      "userVerified": true,
      "backupEligible": false,
      "backupStatus": false,
      "attestedData": true,
      "extensionData": false
    },
    "counter": 0,
    "aaguid": {
      "raw": "08987058-cadc-4b81-b6e1-30de50dcbe96",
      "name": "Windows Hello"
    },
    "credentialID": "GbzVebwHup3tafa6l3zWvcJK5Mqrh113-0Du8jJtGFE",
    "credentialPublicKey": "pQECAyYgASFYIB1OjbHkSpSgqpLtt9WvqRYQVtyAk7ke-hwPZ_LpPYbsIlggeLvi90yPuuB7R0W89LqRJXc8E66uk4ewACCPtFIswNI",
    "parsedCredentialPublicKey": {
      "keyType": "EC2 (2)",
      "algorithm": "ES256 (-7)",
      "curve": 1,
      "x": "HU6NseRKlKCqku231a-pFhBW3ICTuR76HA9n8uk9huw",
      "y": "eLvi90yPuuB7R0W89LqRJXc8E66uk4ewACCPtFIswNI"
    }
  }
}

Problem in the code (exact line)

File: lib/wax/attestation_statement_format/tpm.ex

The ECC clause of parse_pub_area/1 currently reads a stray length named _unique_length before the X coordinate:

  defp parse_pub_area(<<
         @tpm_alg_ecc::unsigned-big-integer-size(16),
         name_alg::unsigned-big-integer-size(16),
         _object_attributes::binary-size(4),
         auth_policy_length::unsigned-big-integer-size(16),
         _auth_policy::binary-size(auth_policy_length),
         # symmetric
         @tpm_alg_null::unsigned-big-integer-size(16),
         _alg_ecc_scheme::unsigned-big-integer-size(16),
         alg_ecc_curve_id::unsigned-big-integer-size(16),
         # kdf
         @tpm_alg_null::unsigned-big-integer-size(16),
         _unique_length::unsigned-big-integer-size(16),  # <-- ❌ stray length (should not be here)
         unique_x_length::unsigned-big-integer-size(16),
         unique_x::binary-size(unique_x_length),
         unique_y_length::unsigned-big-integer-size(16),
         unique_y::binary-size(unique_y_length)
       >>) do
    pub_area =
      {:ok,
       %{
         type: :ecc,
         name_alg: name_alg,
         curve: to_erlang_curve(alg_ecc_curve_id),
         x: unique_x,
         y: unique_y
       }}

    pub_area
  end

Why this is incorrect (short spec trail)

From the Trusted Platform Module Library Part 2: Structures (Version 184, published March 20, 2025)

Section 12.2.4 details the public area structure (TPMT_PUBLIC) via Table 219:

  • Table 219: For the unique parameter, its type is TPMU_PUBLIC_ID
  • Table 212: In the case of the ecc algorithm, the value of the TPMU_PUBLIC_ID is TPMS_ECC_POINT
  • Table 197: TPMS_ECC_POINT has two parameters: x and y. Each have the type TPM2B_ECC_PARAMETER
  • Table 196: TPM2B_ECC_PARAMETER has two parameters: size (16 bits) and buffer (bits based on size parameter)
  • No further tables

I couldn't find a 16 bit length value before the x and y parameters. Though I did notice Table 198 for TPM2B_ECC_POINT does have an extra 16 bits beforehand, but it's not referenced by any other type (it's used several times in part 3 of the TPM spec). Maybe it was used by mistake here?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions