netbirdio/netbird · error

failed to parse signature: %w

Error message

failed to parse signature: %w

What it means

reposign.ParseSignature failed: it json.Unmarshals the .sig file into {signature, timestamp, key_id, algorithm, hash_algo} (reposign/signature.go:17-24). It fails when the file is not this JSON bundle — e.g. a raw Ed25519 signature from openssl, an HTML error page saved as .sig, or a truncated download. Field-level failures include a key_id that is not exactly 16 hex chars and a timestamp that is not valid RFC3339 (KeyID.UnmarshalJSON and time parsing).

Source

Thrown at client/cmd/signer/artifactsign.go:187

	if err != nil {
		return fmt.Errorf("failed to parse artifact public key: %w", err)
	}

	// Read artifact data
	artifactData, err := os.ReadFile(artifactFile)
	if err != nil {
		return fmt.Errorf("read artifact file: %w", err)
	}

	// Read signature
	sigBytes, err := os.ReadFile(signatureFile)
	if err != nil {
		return fmt.Errorf("read signature file: %w", err)
	}

	signature, err := reposign.ParseSignature(sigBytes)
	if err != nil {
		return fmt.Errorf("failed to parse signature: %w", err)
	}

	// Validate artifact
	if err := reposign.ValidateArtifact([]reposign.PublicKey{publicKey}, artifactData, *signature); err != nil {
		return fmt.Errorf("artifact verification failed: %w", err)
	}

	cmd.Println("✅ Artifact signature is valid")
	cmd.Printf("Artifact: %s\n", artifactFile)
	cmd.Printf("Signed by key: %s\n", signature.KeyID)
	cmd.Printf("Signature timestamp: %s\n", signature.Timestamp.Format("2006-01-02 15:04:05 MST"))
	return nil
}

func handleVerifyArtifactKey(cmd *cobra.Command, artifactKeyFile, rootKeyFile, signatureFile, revocationFile string) error {
	cmd.Println("🔍 Verifying artifact key...")

	// Read artifact key data

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Confirm the .sig is the signer's JSON bundle: cat <file>.sig | jq . should show signature, timestamp, key_id, algorithm=ed25519, hash_algo=blake2s
  2. If the signature came from another tool, re-sign with `signer sign-artifact` instead
  3. Re-download the .sig and check it is not an error page or truncated (size sanity check)
  4. Verify key_id is a 16-character hex string and timestamp parses as RFC3339

Example fix

// before (raw openssl signature passed as .sig)
openssl pkeyutl -sign -inkey artifact-key.pem -rawin -in netbird > netbird.sig

// after (signer produces the JSON bundle the verifier parses)
./signer sign-artifact --artifact-key-file artifact-key.pem --artifact-file netbird
Defensive patterns

Strategy: validation

Validate before calling

if !json.Valid(sigBytes) {
    log.Fatal("signature file is not the signer's JSON bundle")
}

Type guard

func looksLikeSignatureBundle(data []byte) bool {
    var s struct {
        Signature []byte  `json:"signature"`
        Timestamp string  `json:"timestamp"`
        KeyID     string  `json:"key_id"`
    }
    return json.Unmarshal(data, &s) == nil && len(s.Signature) > 0 && len(s.KeyID) == 16
}

Prevention

When it happens

Trigger: Using `openssl dgst -sign` output as the .sig; .sig downloaded from a URL that returned a 404 page; hand-edited JSON with a shortened key_id; base64 signature containing invalid characters.

Common situations: Mixing external signing tooling with the NetBird signer format; partial download in CI; someone pretty-printed or re-serialized the JSON and broke a field.

Understand the failure class

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/69413e8f1abda729. Report an issue: GitHub.