basecamp/fizzy · error · ActionPack::WebAuthn::InvalidKeyError
Missing OKP key coordinate
Error message
Missing OKP key coordinate
What it means
ActionPack::WebAuthn::CoseKey raised InvalidKeyError because a COSE OKP key (key type 1, algorithm -8 EdDSA, curve 6 Ed25519) had no entry under label -2, the x-coordinate. WebAuthn authenticators deliver the credential public key as a CBOR-encoded COSE map, and RFC 9053 requires every OKP key to carry its 32-byte x-coordinate under label -2. The library validates the map's shape before handing anything to OpenSSL, so this error means the decoded CBOR was structurally incomplete, not that OpenSSL rejected the key.
Source
Thrown at lib/action_pack/web_authn/cose_key.rb:143
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]
e_bytes = parameters[RSA_E_LABEL]
raise ActionPack::WebAuthn::InvalidKeyError, "Missing RSA key parameters" if n_bytes.nil? || e_bytes.nil?
raise ActionPack::WebAuthn::InvalidKeyError, "RSA key must be at least #{MINIMUM_RSA_KEY_BITS} bits" if n_bytes.bytesize * 8 < MINIMUM_RSA_KEY_BITSView on GitHub (pinned to 7aabe74580)
Solutions
- Inspect the decoded map (CoseKey.decode(bytes).parameters) and confirm label -2 is actually absent, to distinguish a malformed authenticator payload from your own slicing offset being wrong
- Re-check how you slice the credential public key out of attestedCredentialData (16-byte AAGUID + 2-byte big-endian credentialIdLength + credentialId + CBOR map); off-by-one slicing can decode to a map that lost trailing parameters
- If you build the COSE map yourself (tests or firmware), include the Ed25519 coordinate: { 1 => 1, 3 => -8, -1 => 6, -2 => x }
- Rescue ActionPack::WebAuthn::InvalidKeyError around to_openssl_key at registration and fail the ceremony with a client-visible error instead of a 500
Example fix
# before (test fixture): OKP map without the x-coordinate
params = { 1 => 1, 3 => -8, -1 => 6 }
# after: include the 32-byte Ed25519 x-coordinate under label -2
params = { 1 => 1, 3 => -8, -1 => 6, -2 => ed25519_public_bytes } Defensive patterns
Strategy: validation
Validate before calling
cose = ActionPack::WebAuthn::CoseKey.decode(public_key_bytes) x = cose.parameters[ActionPack::WebAuthn::CoseKey::OKP_X_LABEL] return if x.nil? || !x.is_a?(String) || x.bytesize != 32 # reject before conversion key = cose.to_openssl_key
Type guard
def ed25519_cose_key?(cose)
[cose.key_type, cose.algorithm] == [1, -8] &&
cose.parameters[-1] == 6 &&
cose.parameters[-2].is_a?(String) && cose.parameters[-2].bytesize == 32
end Try / catch
begin
openssl_key = cose_key.to_openssl_key
rescue ActionPack::WebAuthn::InvalidKeyError, ActionPack::WebAuthn::UnsupportedKeyTypeError => e
# client-supplied key is unusable: fail the registration ceremony, not a 500
render json: { error: e.message }, status: :unprocessable_entity
end Prevention
- Slice the credential public key using the documented layout (16-byte AAGUID, 2-byte big-endian credential ID length, credential ID, then the CBOR map) instead of fixed offsets
- Build test COSE keys from real authenticator captures or exported OpenSSL keys, not hand-typed maps
- Rescue InvalidKeyError at the registration ceremony boundary so bad keys become a 4xx response
- Log the decoded label keys (parameters.keys) whenever a key fails to convert
When it happens
Trigger: Calling ActionPack::WebAuthn::CoseKey.decode(cbor).to_openssl_key where the decoded map contains {1=>1, 3=>-8, -1=>6} but no -2 entry. Typical sources: a hand-built test COSE key, a truncated or wrongly sliced publicKey field of attestedCredentialData, or a buggy CTAP implementation that omits the coordinate.
Common situations: Writing WebAuthn registration tests with manually constructed COSE maps; parsing authenticatorData with home-grown offset math (misreading the 2-byte credentialIdLength) so the key slice starts mid-CBOR; firmware or emulator authenticators that emit incomplete OKP maps.
Related errors
- Invalid OKP key: #{error.message}
- Invalid EC2 key: #{error.message}
- Unsupported OKP curve: #{curve}
- Missing RSA key parameters
- Unsupported COSE key type/algorithm: #{key_type}/#{algorithm
AI-assisted analysis of basecamp/fizzy@7aabe74580 (2026-08-21).
Data as JSON: /api/errors/9e22e5dc1d9ee4b9.
Report an issue: GitHub.