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

Unsupported OKP curve: #{curve}

Error message

Unsupported OKP curve: #{curve}

What it means

For OKP (Octet Key Pair) keys the library only supports Ed25519: the COSE curve label (-1) must equal 6. Curve 7 (Ed448), 4 (X25519), or a missing/nil value raises UnsupportedKeyTypeError with the value interpolated. Ed448 signatures have a different length and key layout, so they cannot be verified as Ed25519.

Source

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

      # 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
      raise ActionPack::WebAuthn::InvalidKeyError, "Invalid OKP key: #{error.message}"
    end

    def build_rsa_rs256_key
      n_bytes = parameters[RSA_N_LABEL]

View on GitHub (pinned to 7aabe74580)

Solutions

  1. Pre-check parameters[OKP_CURVE_LABEL] == 6 before to_openssl_key and reject with a specific message.
  2. Ensure registration options advertise EdDSA (-8) only alongside authenticators/browsers that create Ed25519 credentials (the dominant case).
  3. Rescue UnsupportedKeyTypeError at registration and guide the user to a different authenticator.
  4. If Ed448 support is required, add a dedicated builder branch — do not coerce the key through the Ed25519 path.

Example fix

# before
key = cose_key.to_openssl_key # raises for curve 7 (Ed448) or nil

# after — gate on Ed25519 explicitly
if cose_key.key_type == CoseKey::OKP && cose_key.parameters[CoseKey::OKP_CURVE_LABEL] != CoseKey::ED25519
  return render json: { error: 'only Ed25519 credentials are supported' }, status: :bad_request
end
key = cose_key.to_openssl_key
Defensive patterns

Strategy: validation

Validate before calling

if cose_key.key_type == ActionPack::WebAuthn::CoseKey::OKP && cose_key.parameters[ActionPack::WebAuthn::CoseKey::OKP_CURVE_LABEL] != ActionPack::WebAuthn::CoseKey::ED25519
  return render json: { error: 'only Ed25519 credentials are supported' }, status: :bad_request
end

Type guard

def supported_okp_curve?(cose_key)
  cose_key.parameters[ActionPack::WebAuthn::CoseKey::OKP_CURVE_LABEL] == ActionPack::WebAuthn::CoseKey::ED25519
end

Try / catch

begin
  key = cose_key.to_openssl_key
rescue ActionPack::WebAuthn::UnsupportedKeyTypeError => e
  render json: { error: 'please use a different security key' }, status: :bad_request
end

Prevention

When it happens

Trigger: A credential created with Ed448 (curve 7) or an OKP key whose -1 label is absent/mis-encoded, followed by to_openssl_key during registration; pubKeyCredParams that advertised alg -8 without pinning the Ed25519 curve.

Common situations: Experimental authenticators or test tools emitting Ed448; COSE maps round-tripped through JSON where label -1 was dropped or stringified; conformance suites probing OKP variants.

Related errors


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