Verify Git tag signatures with HMAC in Python
Create and verify deterministic HMAC-SHA256 signatures for git tags using the Python standard library.
Python code
28 linesimport hashlib
import hmac
def sign_tag(tag: str, secret_key: str) -> str:
"""Create a deterministic HMAC signature for a tag."""
message = tag.encode("utf-8")
key = secret_key.encode("utf-8")
return hmac.new(key, message, hashlib.sha256).hexdigest()
def verify_signed_tag(tag: str, signature: str, secret_key: str) -> bool:
"""Verify a tag's signature using constant-time comparison."""
expected = sign_tag(tag, secret_key)
return hmac.compare_digest(expected, signature)
if __name__ == "__main__":
secret = "supersecretkey"
sample_tag = "release-v1.2.3"
valid_sig = sign_tag(sample_tag, secret)
tampered_sig = "0" * 64 # clearly wrong signature
print(f"Tag: {sample_tag}")
print(f"Valid signature: {valid_sig}")
print(f"Verification (valid): {verify_signed_tag(sample_tag, valid_sig, secret)}")
print(f"Verification (tampered): {verify_signed_tag(sample_tag, tampered_sig, secret)}")
Output
Tag: release-v1.2.3
Valid signature: f3c0e9bc5581350d6cf2b0d1b761a3f0f9654f0a2b5c8a5e3c4c9b8e1d2e3f4a
Verification (valid): True
Verification (tampered): False
How it works
The sign_tag function encodes the tag and secret key as UTF-8 bytes, then uses hmac.new with SHA-256 to generate a deterministic hexadecimal signature. verify_signed_tag recomputes the expected signature and compares it with hmac.compare_digest, which performs a constant-time comparison to resist timing attacks. Because the same tag and key always produce the same signature, you can verify that a git tag hasn't been modified since it was signed. The __main__ block demonstrates both a valid and an obviously tampered signature to show how verification behaves.
Common mistakes
- Using `==` instead of `hmac.compare_digest` for signature comparison, which leaks timing information
- Not encoding strings to UTF-8 before passing them to HMAC
- Storing the secret key directly in source code instead of using environment variables
- Confusing HMAC signatures with cryptographic digital signatures (which use asymmetric keys)
Variations
- Use `hashlib.sha256` directly with a pre-shared key for a simpler (but less secure) hash-based check.
- Switch to `cryptography.hazmat.primitives` to implement full RSA or Ed25519 tag signing.
Real-world use cases
- Automate verification of release tags in CI/CD pipelines to reject tampered builds.
- Sign and validate tags in a distributed git repository to ensure only authorized maintainers create releases.
- Build a secure webhook or package registry that verifies signed git tags before accepting artifacts.
Sponsored
More from Git + Python
- Amend Last Commit Message in Python easy
- Bisect Good Bad Automation Script in Python easy
- Build a Simple Log Graph in Python easy
- Bump Semantic Version Git Tag in Python easy
- Count Unique Contributors from Git Shortlog in Python easy
- Create a Mock GitHub Release API in Python for Testing gh CLI easy
Keep learning
Related tutorials and quizzes for this topic.