basecamp/fizzy · error · ActionPack::WebAuthn::InvalidResponseError

Authenticator data is too short for credential ID and public

Error message

Authenticator data is too short for credential ID and public key

What it means

With attested credential data present, the parser reads a 2-byte credential ID length, then requires the remaining buffer to hold at least credential_id_length bytes plus 1 or more bytes of COSE public key (the +1 guarantees a non-empty key). If the declared ID length overruns the buffer or the public key is missing entirely, InvalidResponseError is raised.

Source

Thrown at lib/action_pack/web_authn/authenticator/data.rb:114

      aaguid = nil
      credential_id = nil
      public_key_bytes = nil

      if flags & ATTESTED_CREDENTIAL_DATA_FLAG != 0
        if bytes.length < position + AAGUID_LENGTH + CREDENTIAL_ID_LENGTH_BYTES
          raise ActionPack::WebAuthn::InvalidResponseError, "Authenticator data is too short for attested credential data"
        end

        aaguid_bytes = bytes[position, AAGUID_LENGTH].pack("C*")
        aaguid = aaguid_bytes.unpack("H8H4H4H4H12").join("-")
        position += AAGUID_LENGTH

        credential_id_length = bytes[position, CREDENTIAL_ID_LENGTH_BYTES].pack("C*").unpack1("n")
        position += CREDENTIAL_ID_LENGTH_BYTES

        if bytes.length < position + credential_id_length + 1
          raise ActionPack::WebAuthn::InvalidResponseError, "Authenticator data is too short for credential ID and public key"
        end

        credential_id = Base64.urlsafe_encode64(bytes[position, credential_id_length].pack("C*"), padding: false)
        position += credential_id_length

        public_key_bytes = bytes[position..].pack("C*")
      end

      new(
        bytes: bytes,
        relying_party_id_hash: relying_party_id_hash,
        flags: flags,
        sign_count: sign_count,
        aaguid: aaguid,
        credential_id: credential_id,
        public_key_bytes: public_key_bytes
      )
    end

View on GitHub (pinned to 7aabe74580)

Solutions

  1. Verify in console: decode the data, check bytesize > 55 + declared credential ID length, and that the trailing bytes form valid CBOR (they decode via CborDecoder.decode).
  2. Pass through the browser's response.getPublicKey()/attestation object bytes untouched instead of re-encoding.
  3. Raise or remove request body-size limits that could clip large credential IDs (some are 255+ bytes, security keys even 1023).
  4. Rescue ActionPack::WebAuthn::InvalidResponseError at the controller boundary and return 400 with a registration-failed message.

Example fix

# before
# reconstructing data field-by-field (breaks byte math)
auth_data = rp_hash + flags + sign_count + aaguid + [id_len].pack('n') + id_bytes # key forgotten

# after — forward the browser's buffer whole
response = ActionPack::WebAuthn::Authenticator::AttestationResponse.new(
  client_data_json: params[:client_data_json],
  authenticator_data: params[:authenticator_data], # full base64url buffer, untouched
  ...
)
Defensive patterns

Strategy: try-catch

Validate before calling

raw = Base64.urlsafe_decode64(params[:authenticator_data].to_s)
if raw.bytesize > 55 && (raw.getbyte(32) & 0x40) != 0
  id_len = raw[53, 2].unpack1('n')
  return render(json: { error: 'credential id/key truncated' }, status: :bad_request) if raw.bytesize < 55 + id_len + 1
end

Type guard

def plausible_attested_credential?(raw)
  return true if (raw.getbyte(32) & 0x40).zero?
  id_len = raw[53, 2].unpack1('n')
  raw.bytesize >= 55 + id_len + 1
end

Try / catch

begin
  response.validate!
rescue ActionPack::WebAuthn::InvalidResponseError => e
  render json: { error: e.message }, status: :bad_request
end

Prevention

When it happens

Trigger: A registration response where the credential ID length field claims more bytes than remain; a truncated attestation that cut off everything after the credential ID; a client that appends the COSE key as text/JSON instead of raw CBOR bytes so the byte math no longer lines up.

Common situations: Buffers truncated at a fixed request-size limit; custom client-side assembly of authenticatorData from parts; byte-slice off-by-one bugs in bespoke parsers feeding this API; fixtures copied from a different authenticator with a different credential ID size.

Understand the failure class

Related errors


AI-assisted analysis of basecamp/fizzy@7aabe74580 (2026-08-21). Data as JSON: /api/errors/c5e3d4cc720a2b4f. Report an issue: GitHub.