netbirdio/netbird · critical

artifact verification failed: %w

Error message

artifact verification failed: %w

What it means

reposign.ValidateArtifact rejected the artifact; this is the security decision of verify-artifact and must never be bypassed. Concrete refusals in reposign/artifact.go:221-265: signature timestamp more than 5 minutes in the future (clock skew), signature older than 10 years, the signing key had expired at signature time, Ed25519 verification of the BLAKE2s hash/length/timestamp message failed (artifact bytes changed or wrong key), or no key with the signature's KeyID is in the supplied key list.

Source

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

	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
	artifactKeyData, err := os.ReadFile(artifactKeyFile)
	if err != nil {
		return fmt.Errorf("read artifact key file: %w", err)
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Confirm the artifact is byte-identical to what was signed: compare sha256sum against the value at signing time
  2. Check the key pair matches: the KeyID printed on successful signing must equal the ID embedded in --artifact-public-key-file
  3. Sync the system clock (max allowed skew is 5 minutes) and re-verify
  4. If the signing key expired, re-sign with a current artifact key and republish
Defensive patterns

Strategy: try-catch

Try / catch

if err := reposign.ValidateArtifact([]reposign.PublicKey{publicKey}, artifactData, *signature); err != nil {
    switch {
    case strings.Contains(err.Error(), "no signing Key found"):
        log.Fatalf("key file does not match signer KeyID %s", signature.KeyID)
    case strings.Contains(err.Error(), "timestamp is in the future"), strings.Contains(err.Error(), "too old"):
        log.Fatalf("clock/timestamp problem: %v", err)
    default:
        log.Fatalf("VERIFICATION FAILED, do not ship: %v", err)
    }
}

Prevention

When it happens

Trigger: Artifact rebuilt but not re-signed (hash mismatch); --artifact-public-key-file is a different key than the signer used ("no signing Key found with ID ..."); host clock more than 5 minutes fast; signing key expired between signing and verifying; artifact or .sig truncated during transfer.

Common situations: Release pipeline re-builds the binary after signing; verifier uses an old/new key rotation mismatch; VM clock drift in CI; partially downloaded artifact.

Related errors


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