antiwork/gumroad · warning

bad_cert

bad_cert

Error message

Attestation rejected.

What it means

WalksAppAttestVerifier.attest never raises on bad input — it returns a Result with valid? false and a stable error symbol. The :bad_cert code maps OpenSSL::X509::CertificateError, OpenSSL::PKey::PKeyError, and ArgumentError from parsing the attestation's x5c chain or key material: structurally invalid certificates, wrong key type, or base64 that decoded to garbage bytes. Earlier shape guards (x5c must be a non-empty array of non-empty strings) already passed, so the DER content itself failed OpenSSL.

Source

Thrown at app/services/walks_app_attest_verifier.rb:97

      return fail_result(:bad_counter) unless parsed[:counter].zero?

      environment = ENVIRONMENT_BY_AAGUID[parsed[:aaguid]]
      return fail_result(:bad_aaguid) unless environment
      return fail_result(:wrong_env_for_rails) unless aaguid_allowed_in_env?(environment)
      return fail_result(:credential_id_mismatch) unless parsed[:credential_id] == credential_id

      key = WalksAppAttestKey.create!(
        key_id: key_id,
        public_key: cred_cert.public_key.to_der,
        environment: environment,
        attested_at: Time.current,
      )
      Result.new(valid?: true, key: key)
    rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid
      fail_result(:duplicate_key)
    rescue OpenSSL::X509::CertificateError, OpenSSL::PKey::PKeyError, ArgumentError => e
      Rails.logger.warn("WalksAppAttestVerifier.attest failed: #{e.class} #{e.message}")
      fail_result(:bad_cert)
    rescue CBOR::MalformedFormatError, EOFError => e
      Rails.logger.warn("WalksAppAttestVerifier.attest CBOR error: #{e.class} #{e.message}")
      fail_result(:bad_cbor)
    end

    def assert(key_id:, assertion_b64:, challenge:, request_body:)
      return fail_result(:missing_key_id) if key_id.blank?
      return fail_result(:missing_assertion) if assertion_b64.blank?
      return fail_result(:missing_challenge) if challenge.blank?

      key = WalksAppAttestKey.find_by(key_id: key_id)
      return fail_result(:unknown_key) unless key

      # Consume the challenge only after the keyId is known to be real.
      # A scraper hitting the endpoint with a random keyId would otherwise
      # be able to burn challenges issued to legitimate devices (single-use
      # Redis nonces). Since keyIds are SHA256(pubkey) — unguessable —
      # gating consume on `find_by` closes the DoS without changing the

View on GitHub (pinned to afeacbd394)

Solutions

  1. On the client, send standard-alphabet base64 (e.g. base64EncodedString()) of the DCAppAttestService.attestKey object untouched — no re-encoding of the CBOR.
  2. Check the Rails warn line "WalksAppAttestVerifier.attest failed: <class> <message>" to see which OpenSSL parse failed.
  3. Compare the received blob byte-for-byte with what the device produced and re-attest with a fresh challenge.
  4. Third-party clients: forward Apple's attestation object as-is; any transformation produces exactly this error.

Example fix

// before: urlsafe base64 / re-encoded payload
let b64 = attestationObject.base64URLEncodedString()

// after: standard base64 of the untouched object
let b64 = attestationObject.base64EncodedString()
Defensive patterns

Strategy: validation

Validate before calling

# Caller: branch on the Result symbol, never on an exception
result = WalksAppAttestVerifier.attest(key_id:, attestation_b64:, challenge:)
return head :unprocessable_entity unless result.valid?
# result.error == :bad_cert means the x5c chain/key failed OpenSSL parsing — client must re-attest with a fresh challenge

Type guard

# Ruby: narrow on the stable error symbol before deciding a response
BAD_REQUEST_ERRORS = %i[missing_key_id missing_attestation invalid_challenge bad_cbor bad_cert wrong_fmt].freeze

 attest_rejected_as_bad_request = ->(r) { !r.valid? && BAD_REQUEST_ERRORS.include?(r.error) }

Try / catch

# Not needed: attest/assert never raise on bad input; every failure path returns
# WalksAppAttestVerifier::Result (valid?: false, error: <symbol>) that the caller logs and maps to 4xx

Prevention

When it happens

Trigger: An iOS client sends an attestation blob whose x5c entries are truncated or corrupted DER, base64 decoded to garbage (wrong alphabet such as urlsafe vs standard, bad padding), a certificate whose public key is not EC P-256, or a proxy mangled the request body between device and server.

Common situations: Client-side base64/CBOR encoding bugs (double encoding, re-encoding the CBOR instead of forwarding Apple's object), tampered attestations from non-genuine clients, test payloads captured from a different environment, or request-body corruption in transit.

Related errors


AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21). Data as JSON: /api/errors/33347c98042173ca. Report an issue: GitHub.