astrid-runtime/astrid · error

fingerprint Ed25519 public key

Error message

fingerprint Ed25519 public key: {e}

What it means

fingerprint_pubkey converts a hex-encoded Ed25519 public key into the short `key-<hex>` fingerprint via astrid_crypto::PublicKeyFingerprint::from_ed25519_hex. When the input is not valid 64-char hex encoding of a 32-byte Ed25519 public key, the fingerprint parser fails and the error is wrapped as "fingerprint Ed25519 public key: {e}".

Solutions

  1. Pass the 64-char hex public key (not the `ed25519:<base64>` wire form) to fingerprinting; convert wire form back to hex first if needed
  2. Trim whitespace and strip any `0x` prefix or PEM headers from the key string before calling
  3. Regenerate the keypair if the stored key is corrupt (astrid keypair generate)

Example fix

// before
let fp = fingerprint_pubkey(&wire)?; // "ed25519:AbCd..." not hex
// after
let fp = fingerprint_pubkey(&hex_pub.trim())?; // 64-char hex
Defensive patterns

Strategy: validation

Validate before calling

fn is_hex_ed25519_pubkey(s: &str) -> bool {
    let s = s.trim();
    s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit())
}

Type guard

fn fingerprintable(s: &str) -> bool {
    let s = s.trim();
    s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit())
}

Try / catch

match fingerprint_pubkey(hex_pub) {
    Ok(fp) => fp,
    Err(e) if e.to_string().starts_with("fingerprint Ed25519 public key") => {
        eprintln!("Key is not 64-char hex; convert wire form or re-copy the key");
        return Err(e);
    }
}

Prevention

When it happens

Trigger: Called from run_generate, read_meta, or fingerprint_is_stable_and_distinct with a string that isn't exactly 64 lowercase/uppercase hex chars (wrong length, non-hex characters, empty string, or the `ed25519:<base64>` wire form passed instead of hex).

Common situations: Storing the base64 wire form in metadata and later fingerprinting it directly; truncated key from copy/paste; reading a key file with trailing whitespace/newlines or a PEM header.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/1674c64f6407d7b5. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-cli/src/commands/keypair.rs:607

        .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
    {
        bail!("keypair name {name:?} contains invalid chars; only a-z, 0-9, '-' are allowed");
    }
    Ok(())
}

fn default_name() -> String {
    let mut bytes = [0u8; 4];
    SysRng
        .try_fill_bytes(&mut bytes)
        .expect("OS CSPRNG unavailable while generating default keypair name");
    format!("key-{}", hex::encode(bytes))
}

fn fingerprint_pubkey(hex_pub: &str) -> Result<String> {
    PublicKeyFingerprint::from_ed25519_hex(hex_pub)
        .map(PublicKeyFingerprint::into_inner)
        .map_err(|e| anyhow::anyhow!("fingerprint Ed25519 public key: {e}"))
}

/// Convert a 64-char hex ed25519 public key into the `ed25519:<base64>`
/// wire form that `[distro.signing].pubkey`, `astrid distro seal`, and
/// the distro trust store consume. Reuses `astrid-crypto`'s encoder so
/// the base64 variant matches the verifier byte-for-byte.
fn pubkey_hex_to_wire(pub_hex: &str) -> Result<String> {
    let pk = astrid_crypto::PublicKey::from_hex(pub_hex.trim())
        .map_err(|e| anyhow::anyhow!("decode public key hex: {e}"))?;
    Ok(format!("ed25519:{}", pk.to_base64()))
}

/// Encode a 32-byte ed25519 public key in the `OpenSSH` wire format
/// (`ssh-ed25519 <base64>` — RFC 8709 §4). Lets operators paste the
/// same key into `authorized_keys` if they want to reuse it for SSH.
/// The body is a length-prefixed type tag followed by the key.
fn encode_openssh_ed25519(pubkey: &[u8]) -> String {
    use base64::Engine;

View on GitHub (pinned to affd8760f4)