zeroclaw-labs/zeroclaw · error · anyhow::Error
Unable to extract public key from attestation object ({} byt
Error message
Unable to extract public key from attestation object ({} bytes) What it means
During finish_registration, extract_public_key_from_attestation walks the CBOR attestation object looking for a COSE key; if none is found (or the layout is unexpected) it bails, reporting the attestation byte length (webauthn.rs:643-649). Registration cannot proceed without the public key.
Source
Thrown at crates/zeroclaw-runtime/src/security/webauthn.rs:648
attestation_bytes[36],
]);
// Check if attested credential data is present (bit 6 of flags)
let flags = attestation_bytes[32];
if flags & 0x40 != 0 && attestation_bytes.len() > 55 {
// AAGUID (16) + credIdLen (2) + credId (variable) + COSE key
let cred_id_len =
u16::from_be_bytes([attestation_bytes[53], attestation_bytes[54]]) as usize;
let cose_key_start = 55 + cred_id_len;
if attestation_bytes.len() > cose_key_start {
let cose_key = &attestation_bytes[cose_key_start..];
let pk = extract_p256_from_cose(cose_key)?;
return Ok((pk, sign_count));
}
}
}
anyhow::bail!(
"Unable to extract public key from attestation object ({} bytes)",
attestation_bytes.len()
)
}
/// Simplified attestation object for the enrollment UI.
#[derive(Deserialize)]
struct AttestationObject {
/// Base64url-encoded public key (uncompressed P-256 or DER SPKI).
public_key: String,
/// Initial sign counter.
sign_count: Option<u32>,
}
fn extract_p256_from_cose(cose: &[u8]) -> Result<Vec<u8>> {
// If it starts with 0x04 and is 65 bytes, it's already uncompressed P-256
if cose.len() >= 65 && cose[0] == 0x04 {
return Ok(cose[..65].to_vec());View on GitHub (pinned to 88bb9c8533)
Solutions
- Log the attestation format identifier ('fmt') and byte length, then retry registration — many authenticators work on retry with a different format
- Ensure the client passes the raw base64url attestationObject without re-encoding
- If one authenticator model always fails, capture its attestation object and check whether the COSE key is embedded in a different CBOR layout
Defensive patterns
Strategy: try-catch
Try / catch
Catch around finish_registration, return a user-facing 'registration failed — try again or use another passkey/security key', and log the attestation format and byte length (not the raw object) for diagnostics.
Prevention
- Pass authenticator responses through byte-for-byte — never re-serialize them
- Test enrollment with more than one browser and authenticator model
- Log WebAuthn clientData type and origin alongside failures to spot client-side bugs
When it happens
Trigger: finish_registration with an attestation object that lacks a COSE key in the expected position: a truncated authenticator response, or an attestation statement layout the extractor does not traverse.
Common situations: A browser/authenticator pair produces an attestation format outside the parsed paths; upstream base64url decoding dropped bytes; the client deserialized and re-serialized the response incorrectly.
Related errors
- Expected type 'webauthn.create', got '{cd_type}'
- Challenge mismatch in registration response
- Origin mismatch: expected '{}', got '{cd_origin}'
- Credential ID too long ({} bytes, max {MAX_CREDENTIAL_ID_LEN
- No credentials found for user '{user_id}'
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/663ed118b2b525d5.
Report an issue: GitHub.