{"record":{"id":"825774d3df2aac90","repo":"jdx/mise","slug":"no-trusted-public-keys-available-for-verification","errorCode":null,"errorMessage":"no trusted public keys available for verification","messagePattern":"no trusted public keys available for verification","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/gpg.rs","lineNumber":41,"sourceCode":"}\n\n/// Verify a detached signature entirely in-process (no external `gpg` binary).\n///\n/// `public_keys_asc` is one or more ASCII-armored public key blocks (a trusted keyring bundled\n/// with mise). `open_data` returns a fresh reader over the signed content each time it is called,\n/// so the content can be streamed (and, if necessary, re-read for another candidate key) without\n/// buffering large files in memory.\n///\n/// Verification succeeds if any signature validates against any of the trusted keys or their\n/// subkeys, mirroring `gpg --verify` against an imported keyring.\nfn verify_detached<R, F>(public_keys_asc: &str, signature: &[u8], open_data: F) -> Result<()>\nwhere\n    R: Read,\n    F: Fn() -> Result<R>,\n{\n    let keys = parse_public_keys(public_keys_asc)?;\n    if keys.is_empty() {\n        bail!(\"no trusted public keys available for verification\");\n    }\n    let signatures = parse_signatures(signature)?;\n    if signatures.is_empty() {\n        bail!(\"no signature found to verify\");\n    }\n\n    // Fast path: only try keys whose id/fingerprint matches the signature's issuer, so the signed\n    // content is hashed at most once in the common case.\n    for sig in &signatures {\n        if verify_against_keys(sig, &keys, &open_data, true)? {\n            return Ok(());\n        }\n    }\n    // Fallback: try every trusted key, but only for signatures that carried no usable issuer\n    // hint. A signature that named an issuer we don't trust is genuinely unverifiable, so skip it\n    // rather than re-hashing the (potentially large) content against every key.\n    for sig in signatures.iter().filter(|sig| !has_issuer(&sig.signature)) {\n        if verify_against_keys(sig, &keys, &open_data, false)? {","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/gpg.rs#L23-L59","documentation":"`verify_detached` in gpg.rs verifies a detached signature against a set of trusted public keys. Before doing any cryptographic work it parses the provided armored key material and, if no public keys could be parsed out of it, bails with \"no trusted public keys available for verification\". Empty trusted-key input can never succeed, so it fails fast.","triggerScenarios":"Calling verify_detached (directly or via verify_node/verify_swift/verify_swift_bytes) with a public_keys_asc string/blob containing no parseable keys — empty string, whitespace, no armored blocks, or only corrupted/non-PGP content.","commonSituations":"A settings/config key list pointing at a file that is empty or was truncated; pasting the signature instead of the key; downloading a keyring and getting an HTML error page; a tool upgrade renaming key config so the old lookup yields nothing.","solutions":["Check that the public key source (file/setting) is non-empty and actually contains ASCII-armored PGP public key blocks (-----BEGIN PGP PUBLIC KEY BLOCK-----).","Re-download the official keyring (e.g. nodejs.org dist keys) and update your configured keys.","Fix the config path/setting name that selects the trusted keys — a wrong path often yields empty content.","If keys are provided programmatically, assert the parse result is non-empty before calling verify."],"exampleFix":"// before\nlet keys = std::fs::read_to_string(\"trusted.asc\")?; // file may be empty/HTML\nverify_detached(&keys, &sig, &data)?;\n// after\nlet keys = std::fs::read_to_string(\"trusted.asc\")?;\neyre::ensure!(keys.contains(\"BEGIN PGP PUBLIC KEY BLOCK\"), \"keyring missing armored keys\");\nverify_detached(&keys, &sig, &data)?;","handlingStrategy":"validation","validationCode":"fn has_armored_public_keys(blob: &str) -> bool {\n    blob.contains(\"-----BEGIN PGP PUBLIC KEY BLOCK-----\")\n}\n// call before verify_detached\nassert!(has_armored_public_keys(&public_keys_asc), \"trusted key material empty or malformed\");","typeGuard":null,"tryCatchPattern":"match verify_detached(&keys, &sig, &data) {\n    Err(e) if e.to_string().contains(\"no trusted public keys\") => {\n        eprintln!(\"keyring empty; re-download official keys\");\n        let keys = refresh_official_keyring()?;\n        verify_detached(&keys, &sig, &data)\n    }\n    other => other,\n}","preventionTips":["Verify key files are non-empty and armored before configuring them","Re-download official project keyrings when tool versions change","Fail fast on empty key config in setup scripts rather than at verify time"],"tags":["gpg","verification","keys","security","config"],"backgroundTag":"missing-credentials","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}