basecamp/fizzy · error · ActionPack::WebAuthn::InvalidKeyError
Missing EC2 key coordinates
Error message
Missing EC2 key coordinates
What it means
Building an EC2/ES256 key requires the x (-2) and y (-3) coordinate byte strings from the COSE map. If either label is missing (nil), InvalidKeyError 'Missing EC2 key coordinates' is raised before any OpenSSL work happens. In practice the map itself was malformed or mis-parsed, since real authenticators always include both coordinates.
Source
Thrown at lib/action_pack/web_authn/cose_key.rb:119
# Raises +UnsupportedKeyTypeError+ if the key type, algorithm, or curve
# is not supported.
def to_openssl_key
case [ key_type, algorithm ]
when [ EC2, ES256 ] then build_ec2_es256_key
when [ OKP, EDDSA ] then build_okp_eddsa_key
when [ RSA, RS256 ] then build_rsa_rs256_key
else raise ActionPack::WebAuthn::UnsupportedKeyTypeError, "Unsupported COSE key type/algorithm: #{key_type}/#{algorithm}"
end
end
private
def build_ec2_es256_key
curve = parameters[EC2_CURVE_LABEL]
raise ActionPack::WebAuthn::UnsupportedKeyTypeError, "Unsupported EC curve: #{curve}" unless curve == P256
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
View on GitHub (pinned to 7aabe74580)
Solutions
- Confirm the input to CoseKey.decode is exactly public_key_bytes from Authenticator::Data (bytes after the credential ID) — not the whole attested block.
- Inspect the decoded map: it must contain integer keys 1, 3, -1, -2, -3 with byte-string values.
- If coordinates crossed a JSON layer, re-encode binary values as base64url and decode back to ASCII-8BIT before decoding COSE.
- Rescue InvalidKeyError and fail the registration with a clear message; never substitute a default key.
Example fix
# before # decoding from the wrong offset (includes credential-id bytes) cose_key = ActionPack::WebAuthn::CoseKey.decode(attestation_bytes[id_offset..]) # after — use the parsed authenticator data's key slice auth_data = ActionPack::WebAuthn::Authenticator::Data.wrap(params[:authenticator_data]) cose_key = ActionPack::WebAuthn::CoseKey.decode(auth_data.public_key_bytes)
Defensive patterns
Strategy: validation
Validate before calling
params_map = cose_key.parameters
missing = [ -1, -2, -3 ].reject { |label| params_map[label].is_a?(String) }
return render(json: { error: 'credential key incomplete' }, status: :bad_request) if missing.any? && cose_key.key_type == ActionPack::WebAuthn::CoseKey::EC2 Type guard
def complete_ec2_cose_key?(cose_key)
!cose_key.parameters[ActionPack::WebAuthn::CoseKey::EC2_X_LABEL].nil? &&
!cose_key.parameters[ActionPack::WebAuthn::CoseKey::EC2_Y_LABEL].nil?
end Try / catch
begin
key = cose_key.to_openssl_key
rescue ActionPack::WebAuthn::InvalidKeyError => e
render json: { error: 'credential key invalid' }, status: :bad_request
end Prevention
- Always extract the key as authenticator_data.public_key_bytes — never slice raw attestation bytes yourself.
- Keep COSE maps in CBOR end-to-end; JSON relays must encode byte strings as base64url, not drop them.
- Fail registration on missing coordinates — there is no safe default.
When it happens
Trigger: CoseKey.decode over a CBOR map that lacks label -2 or -3 — e.g. the wrong byte slice was decoded as the key (offset shifted past the map), a truncated attested credential data block, or a fixture map copied incompletely.
Common situations: Custom code that extracts public_key_bytes from authenticator data with wrong offsets; fixtures hand-built with only kty/alg/crv; CBOR maps whose labels were encoded as strings ('-2') instead of integers during relay through JSON.
Related errors
- Unsupported COSE key type/algorithm: #{key_type}/#{algorithm
- Unsupported EC curve: #{curve}
- Invalid EC2 coordinate length
- Invalid EC2 key: #{error.message}
- Unsupported OKP curve: #{curve}
AI-assisted analysis of basecamp/fizzy@7aabe74580 (2026-08-21).
Data as JSON: /api/errors/5e217e2f4d9447a0.
Report an issue: GitHub.