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

Invalid EC2 key: #{error.message}

Error message

Invalid EC2 key: #{error.message}

What it means

When the coordinate lengths pass, the library packs 0x04||x||y into ASN.1 and hands it to OpenSSL::PKey::EC.new. If OpenSSL rejects it — most commonly because the point is not on the P-256 curve — the OpenSSL::PKey::PKeyError is re-raised as InvalidKeyError 'Invalid EC2 key: <openssl message>'. Structurally valid bytes but mathematically invalid key material.

Source

Thrown at lib/action_pack/web_authn/cose_key.rb:135

      x = parameters[EC2_X_LABEL]
      y = parameters[EC2_Y_LABEL]
      raise ActionPack::WebAuthn::InvalidKeyError, "Missing EC2 key coordinates" if x.nil? || y.nil?
      raise ActionPack::WebAuthn::InvalidKeyError, "Invalid EC2 coordinate length" unless x.bytesize == P256_COORDINATE_LENGTH && y.bytesize == P256_COORDINATE_LENGTH

      # Uncompressed point format: 0x04 || x || y
      public_key_bytes = [ UNCOMPRESSED_POINT_MARKER, *x.bytes, *y.bytes ].pack("C*")

      asn1 = OpenSSL::ASN1::Sequence([
        OpenSSL::ASN1::Sequence([
          OpenSSL::ASN1::ObjectId("id-ecPublicKey"),
          OpenSSL::ASN1::ObjectId("prime256v1")
        ]),
        OpenSSL::ASN1::BitString(public_key_bytes)
      ])

      OpenSSL::PKey::EC.new(asn1.to_der)
    rescue OpenSSL::PKey::PKeyError => error
      raise ActionPack::WebAuthn::InvalidKeyError, "Invalid EC2 key: #{error.message}"
    end

    def build_okp_eddsa_key
      curve = parameters[OKP_CURVE_LABEL]
      raise ActionPack::WebAuthn::UnsupportedKeyTypeError, "Unsupported OKP curve: #{curve}" unless curve == ED25519

      x = parameters[OKP_X_LABEL]
      raise ActionPack::WebAuthn::InvalidKeyError, "Missing OKP key coordinate" if x.nil?

      asn1 = OpenSSL::ASN1::Sequence([
        OpenSSL::ASN1::Sequence([
          OpenSSL::ASN1::ObjectId("ED25519")
        ]),
        OpenSSL::ASN1::BitString(x)
      ])

      OpenSSL::PKey.read(asn1.to_der)
    rescue OpenSSL::PKey::PKeyError => error

View on GitHub (pinned to 7aabe74580)

Solutions

  1. Treat it as a failed registration: rescue InvalidKeyError, log the OpenSSL message, and return 400 — never fall back to partial verification.
  2. If it reproduces with one specific authenticator, capture a fresh registration from it and compare coordinates; a consistent failure indicates a firmware/encoder bug.
  3. Verify the transport path for byte corruption (compare the client-side hash of the key bytes with the server-side one).
  4. In tests, generate valid points with OpenSSL::PKey::EC.new('prime256v1').generate_key instead of random bytes.

Example fix

# before
key = cose_key.to_openssl_key # raises InvalidKeyError: point not on curve

# after — fail registration cleanly on any invalid key material
begin
  key = cose_key.to_openssl_key
rescue ActionPack::WebAuthn::InvalidKeyError => e
  Rails.logger.warn { "Rejected invalid credential key: #{e.message}" }
  return render json: { error: 'credential key invalid' }, status: :bad_request
end
Defensive patterns

Strategy: try-catch

Try / catch

begin
  key = cose_key.to_openssl_key
rescue ActionPack::WebAuthn::InvalidKeyError => e
  Rails.logger.warn { "Rejected invalid EC2 key: #{e.message}" }
  render json: { error: 'credential key invalid' }, status: :bad_request
end

Prevention

When it happens

Trigger: Coordinates that are each exactly 32 bytes but do not satisfy the curve equation y^2 = x^3 - 3x + b mod p — corrupt values, bit-flipped transport, forged attestation attempts, or random test bytes used as coordinates.

Common situations: Fuzz tests feeding random 32-byte strings; fixtures where x/y were swapped or truncated-then-padded incorrectly; tampered registration payloads; authenticator firmware bugs (rare).

Related errors


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