{"record":{"id":"5ad3f572f46a8dd0","repo":"basecamp/fizzy","slug":"invalid-okp-key-error-message","errorCode":null,"errorMessage":"Invalid OKP key: #{error.message}","messagePattern":"Invalid OKP key: #(.+?)","errorType":"exception","errorClass":"ActionPack::WebAuthn::InvalidKeyError","httpStatus":null,"severity":"error","filePath":"lib/action_pack/web_authn/cose_key.rb","lineNumber":154,"sourceCode":"    end\n\n    def build_okp_eddsa_key\n      curve = parameters[OKP_CURVE_LABEL]\n      raise ActionPack::WebAuthn::UnsupportedKeyTypeError, \"Unsupported OKP curve: #{curve}\" unless curve == ED25519\n\n      x = parameters[OKP_X_LABEL]\n      raise ActionPack::WebAuthn::InvalidKeyError, \"Missing OKP key coordinate\" if x.nil?\n\n      asn1 = OpenSSL::ASN1::Sequence([\n        OpenSSL::ASN1::Sequence([\n          OpenSSL::ASN1::ObjectId(\"ED25519\")\n        ]),\n        OpenSSL::ASN1::BitString(x)\n      ])\n\n      OpenSSL::PKey.read(asn1.to_der)\n    rescue OpenSSL::PKey::PKeyError => error\n      raise ActionPack::WebAuthn::InvalidKeyError, \"Invalid OKP key: #{error.message}\"\n    end\n\n    def build_rsa_rs256_key\n      n_bytes = parameters[RSA_N_LABEL]\n      e_bytes = parameters[RSA_E_LABEL]\n      raise ActionPack::WebAuthn::InvalidKeyError, \"Missing RSA key parameters\" if n_bytes.nil? || e_bytes.nil?\n      raise ActionPack::WebAuthn::InvalidKeyError, \"RSA key must be at least #{MINIMUM_RSA_KEY_BITS} bits\" if n_bytes.bytesize * 8 < MINIMUM_RSA_KEY_BITS\n\n      n = OpenSSL::BN.new(n_bytes, 2)\n      e = OpenSSL::BN.new(e_bytes, 2)\n\n      asn1 = OpenSSL::ASN1::Sequence([\n        OpenSSL::ASN1::Sequence([\n          OpenSSL::ASN1::ObjectId(\"rsaEncryption\"),\n          OpenSSL::ASN1::Null.new(nil)\n        ]),\n        OpenSSL::ASN1::BitString(\n          OpenSSL::ASN1::Sequence([","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/basecamp/fizzy/blob/7aabe7458060d8a1759a53b7ede39e74e6c0b20d/lib/action_pack/web_authn/cose_key.rb#L136-L172","documentation":"ActionPack::WebAuthn::CoseKey wrapped an OpenSSL::PKey::PKeyError raised while reading an Ed25519 public key: the COSE map did carry an x-coordinate (label -2), but the DER SubjectPublicKeyInfo built from it failed OpenSSL::PKey.read. The usual causes are that the coordinate is not exactly 32 raw bytes (wrong CBOR type, hex text, truncation) or that the linked OpenSSL/LibreSSL lacks Ed25519 support (needs OpenSSL 1.1.1 or later). The library re-raises as InvalidKeyError with the original OpenSSL message appended, e.g. 'Invalid OKP key: ...'.","triggerScenarios":"Calling to_openssl_key on an OKP/EdDSA key whose -2 value is present but not a 32-byte binary String (an Integer, hex text, or wrong length), so OpenSSL::PKey.read raises; or running on a Ruby linked against OpenSSL < 1.1.1 or LibreSSL without Ed25519, where any Ed25519 SPKI read fails regardless of the coordinate.","commonSituations":"Enabling EdDSA passkey support on a host with an old OpenSSL (CentOS 7 OpenSSL 1.0.2, macOS system Ruby with LibreSSL); CBOR or JSON layers returning the coordinate as a hex string instead of bytes; base64url mis-decoding (lost padding) truncating the key bytes in transit.","solutions":["Check what -2 actually contains after decode: it must be a 32-byte binary String; convert hex or base64url input with pack('H*') or Base64.urlsafe_decode64 before building the COSE map","Verify your Ruby's OpenSSL supports Ed25519: ruby -ropenssl -e 'puts OpenSSL::OPENSSL_LIBRARY_VERSION' (needs OpenSSL >= 1.1.1); on macOS system Ruby or old Linux, switch to a Ruby build linked against modern OpenSSL","Rescue ActionPack::WebAuthn::InvalidKeyError at the registration boundary and reject the credential when Ed25519 conversion fails","For fixtures, take the x-coordinate from a real Ed25519 public key rather than random bytes"],"exampleFix":"# before: coordinate ends up as hex text, so OpenSSL::PKey.read fails\nx = '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60'\n\n# after: pack to the raw 32 bytes the COSE label expects\nx = ['9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60'].pack('H*')","handlingStrategy":"try-catch","validationCode":"x = cose.parameters[-2]\nraise ArgumentError, 'bad Ed25519 coordinate' unless x.is_a?(String) && x.bytesize == 32\n# Ed25519 requires OpenSSL >= 1.1.1\nversion = (OpenSSL::OPENSSL_LIBRARY_VERSION rescue OpenSSL::OPENSSL_VERSION)\nraise LoadError, 'OpenSSL lacks Ed25519 support' unless version =~ /OpenSSL (1\\.1\\.1|3\\.)/","typeGuard":null,"tryCatchPattern":"begin\n  openssl_key = cose_key.to_openssl_key\nrescue ActionPack::WebAuthn::InvalidKeyError => e\n  # error.message carries the underlying OpenSSL reason; fail the ceremony cleanly\n  render json: { error: e.message }, status: :unprocessable_entity\nend","preventionTips":["Confirm runtime OpenSSL is >= 1.1.1 (ruby -ropenssl -e 'puts OpenSSL::OPENSSL_LIBRARY_VERSION') before enabling EdDSA","Keep COSE byte strings binary end to end: decode base64url at the transport edge and never hex-ify parameters","Validate that x is a 32-byte String before calling to_openssl_key","Exercise Ed25519 credentials in CI with a captured real key, since local OpenSSL may differ from production"],"tags":["webauthn","cose","openssl","ed25519","okp"],"backgroundTag":"openssl-key-parse-failure","analyzedSha":"7aabe7458060d8a1759a53b7ede39e74e6c0b20d","analyzedAt":"2026-08-21T18:33:25.349Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}