Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 129 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,35 +47,153 @@ benchmark:

# Binaries cache :
BASE_URL = https://github.com/AikidoSec/zen-internals/releases/download/v0.1.60
FILES = \
BINARY_FILES = \
libzen_internals_aarch64-apple-darwin.dylib \
libzen_internals_aarch64-apple-darwin.dylib.sha256sum \
libzen_internals_aarch64-unknown-linux-gnu.so \
libzen_internals_aarch64-unknown-linux-gnu.so.sha256sum \
libzen_internals_x86_64-apple-darwin.dylib \
libzen_internals_x86_64-apple-darwin.dylib.sha256sum \
libzen_internals_x86_64-pc-windows-gnu.dll \
libzen_internals_x86_64-pc-windows-gnu.dll.sha256sum \
libzen_internals_x86_64-unknown-linux-gnu.so \
libzen_internals_x86_64-unknown-linux-gnu.so.sha256sum
libzen_internals_x86_64-unknown-linux-gnu.so

binaries: binaries_make_dir $(addprefix .cache/binaries/, $(FILES))
DIGEST_FILE = binary_digests.txt

binaries: binaries_make_dir $(addprefix .cache/binaries/, $(BINARY_FILES)) verify_binaries
binaries_make_dir:
rm -rf .cache/binaries
mkdir -p .cache/binaries/
.cache/binaries/%:
@echo "Downloading $*..."
curl -L -o $@ $(BASE_URL)/$*

.PHONY: verify_binaries
verify_binaries:
@echo "Verifying binary integrity against trusted digests..."
@if [ ! -f "$(DIGEST_FILE)" ]; then \
echo "ERROR: Trusted digest file $(DIGEST_FILE) not found."; \
echo "Cannot verify binary integrity. Build aborted."; \
exit 1; \
fi
@if grep -q "PLACEHOLDER_DIGEST_MUST_BE_REPLACED_BEFORE_BUILD" "$(DIGEST_FILE)"; then \
echo "ERROR: $(DIGEST_FILE) contains placeholder digests."; \
echo ""; \
echo "To initialize trusted digests, choose one of:"; \
echo ""; \
echo "Option 1 (Recommended): Download and compute digests independently"; \
echo " python3 scripts/compute_binary_digests.py"; \
echo ""; \
echo "Option 2: Initialize from upstream .sha256sum files (faster, less secure)"; \
echo " python3 scripts/init_digests_from_upstream.py"; \
echo ""; \
echo "After initialization:"; \
echo " 1. Review the computed digests in $(DIGEST_FILE)"; \
echo " 2. Verify binaries are from a trusted source"; \
echo " 3. Commit the changes with a clear audit trail"; \
echo ""; \
echo "Build aborted for security reasons."; \
exit 1; \
fi
@cd .cache/binaries && \
for binary in $(BINARY_FILES); do \
if [ ! -f "$$binary" ]; then \
echo "ERROR: Binary $$binary not found in cache."; \
exit 1; \
fi; \
expected_digest=$$(grep "$$binary$$" ../../$(DIGEST_FILE) | awk '{print $$1}'); \
if [ -z "$$expected_digest" ]; then \
echo "ERROR: No trusted digest found for $$binary in $(DIGEST_FILE)."; \
echo "Cannot verify binary integrity. Build aborted."; \
exit 1; \
fi; \
if command -v sha256sum >/dev/null 2>&1; then \
actual_digest=$$(sha256sum "$$binary" | awk '{print $$1}'); \
elif command -v shasum >/dev/null 2>&1; then \
actual_digest=$$(shasum -a 256 "$$binary" | awk '{print $$1}'); \
else \
echo "ERROR: Neither sha256sum nor shasum found. Cannot verify binaries."; \
exit 1; \
fi; \
if [ "$$actual_digest" != "$$expected_digest" ]; then \
echo "ERROR: Digest mismatch for $$binary"; \
echo " Expected: $$expected_digest"; \
echo " Actual: $$actual_digest"; \
echo ""; \
echo "Binary integrity verification FAILED. Build aborted."; \
echo "This may indicate:"; \
echo " - A compromised download or man-in-the-middle attack"; \
echo " - Corrupted cache files"; \
echo " - Outdated digest file"; \
echo ""; \
echo "To resolve:"; \
echo " 1. Clear the cache: make clean"; \
echo " 2. Verify $(DIGEST_FILE) is up to date"; \
echo " 3. Re-run the build"; \
exit 1; \
fi; \
echo "✓ $$binary verified successfully"; \
done
@echo "All binaries verified successfully against trusted digests."

.PHONY: check_binaries
check_binaries:
@if [ -d "$(CACHE_DIR)" ]; then \
echo "Directory $(CACHE_DIR) exists."; \
@if [ -d "$(CACHE_DIR)" ] && [ -n "$$(ls -A $(CACHE_DIR) 2>/dev/null)" ]; then \
echo "Cache directory $(CACHE_DIR) exists. Verifying cached binaries..."; \
$(MAKE) verify_binaries || { \
echo "Cached binaries failed verification. Re-downloading..."; \
$(MAKE) binaries; \
}; \
else \
echo "Directory $(CACHE_DIR) is empty. Running 'make binaries'..."; \
echo "Cache directory $(CACHE_DIR) is empty or missing. Running 'make binaries'..."; \
$(MAKE) binaries; \
fi

.PHONY: download_and_compute_digests
download_and_compute_digests:
@echo "Downloading binaries and computing digests..."
@echo "WARNING: This will download binaries from $(BASE_URL)"
@echo "Ensure you trust this source before proceeding."
@read -p "Continue? [y/N] " -n 1 -r; \
echo; \
if [[ ! $$REPLY =~ ^[Yy]$$ ]]; then \
echo "Aborted."; \
exit 1; \
fi
@$(MAKE) binaries_make_dir
@for binary in $(BINARY_FILES); do \
echo "Downloading $$binary..."; \
curl -L -o .cache/binaries/$$binary $(BASE_URL)/$$binary || { \
echo "ERROR: Failed to download $$binary"; \
exit 1; \
}; \
done
@echo ""
@echo "Computing SHA256 digests..."
@echo "# Trusted SHA256 digests for zen-internals v0.1.60 binaries" > $(DIGEST_FILE).new
@echo "# Format: <sha256> <filename>" >> $(DIGEST_FILE).new
@echo "# These digests are independently verified and committed to the repository" >> $(DIGEST_FILE).new
@echo "# Any mismatch will cause the build to fail" >> $(DIGEST_FILE).new
@echo "" >> $(DIGEST_FILE).new
@echo "# Generated on $$(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> $(DIGEST_FILE).new
@echo "# Source: $(BASE_URL)" >> $(DIGEST_FILE).new
@echo "" >> $(DIGEST_FILE).new
@cd .cache/binaries && \
for binary in $(BINARY_FILES); do \
if command -v sha256sum >/dev/null 2>&1; then \
digest=$$(sha256sum "$$binary" | awk '{print $$1}'); \
elif command -v shasum >/dev/null 2>&1; then \
digest=$$(shasum -a 256 "$$binary" | awk '{print $$1}'); \
else \
echo "ERROR: Neither sha256sum nor shasum found. Cannot compute digests."; \
exit 1; \
fi; \
echo "$$digest $$binary" >> ../../$(DIGEST_FILE).new; \
echo " $$binary: $$digest"; \
done
@echo ""
@echo "Digests computed and saved to $(DIGEST_FILE).new"
@echo "IMPORTANT: Review the digests, verify they match trusted sources,"
@echo "then replace $(DIGEST_FILE) with $(DIGEST_FILE).new"
@echo ""
@echo "To apply: mv $(DIGEST_FILE).new $(DIGEST_FILE)"


# Replace version number automatically on publish :
replace_version:
Expand Down
32 changes: 32 additions & 0 deletions binary_digests.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Trusted SHA256 digests for zen-internals v0.1.60 binaries
# Format: <sha256> <filename>
# These digests are independently verified and committed to the repository
# Any mismatch will cause the build to fail

# INITIALIZATION REQUIRED:
# Before merging this security fix, run one of:
# python3 scripts/init_digests_from_upstream.py (faster, uses upstream checksums)
# python3 scripts/compute_binary_digests.py (more thorough, computes independently)
#
# Then review the digests and commit the updated file.

# When upgrading zen-internals version:
# 1. Update BASE_URL in Makefile and scripts/*.py
# 2. Verify the release authenticity (GPG signature, release notes, security advisory)
# 3. Run: python3 scripts/compute_binary_digests.py
# 4. Review and commit the updated digests

# macOS ARM64 (Apple Silicon)
PLACEHOLDER_DIGEST_MUST_BE_REPLACED_BEFORE_BUILD libzen_internals_aarch64-apple-darwin.dylib

# Linux ARM64
PLACEHOLDER_DIGEST_MUST_BE_REPLACED_BEFORE_BUILD libzen_internals_aarch64-unknown-linux-gnu.so

# macOS x86_64 (Intel)
PLACEHOLDER_DIGEST_MUST_BE_REPLACED_BEFORE_BUILD libzen_internals_x86_64-apple-darwin.dylib

# Windows x86_64
PLACEHOLDER_DIGEST_MUST_BE_REPLACED_BEFORE_BUILD libzen_internals_x86_64-pc-windows-gnu.dll

# Linux x86_64
PLACEHOLDER_DIGEST_MUST_BE_REPLACED_BEFORE_BUILD libzen_internals_x86_64-unknown-linux-gnu.so
19 changes: 19 additions & 0 deletions scripts/check-digests-hook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
# Pre-commit hook to prevent committing placeholder digests
# Install: cp scripts/check-digests-hook.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit

DIGEST_FILE="binary_digests.txt"

if [ -f "$DIGEST_FILE" ]; then
if grep -q "PLACEHOLDER_DIGEST_MUST_BE_REPLACED_BEFORE_BUILD" "$DIGEST_FILE"; then
echo "ERROR: Cannot commit $DIGEST_FILE with placeholder digests"
echo ""
echo "Please initialize the digests first:"
echo " python3 scripts/compute_binary_digests.py"
echo ""
echo "Then review and stage the updated file."
exit 1
fi
fi

exit 0
135 changes: 135 additions & 0 deletions scripts/compute_binary_digests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""
Script to download zen-internals binaries and compute their SHA256 digests.
This script should be run by a trusted maintainer when updating binary versions.

The script downloads binaries, computes their digests, and also downloads the
upstream .sha256sum files for comparison. The maintainer should independently
verify the binaries before committing the digests.
"""

import hashlib
import sys
import urllib.request
from pathlib import Path

BASE_URL = "https://github.com/AikidoSec/zen-internals/releases/download/v0.1.60"
BINARY_FILES = [
"libzen_internals_aarch64-apple-darwin.dylib",
"libzen_internals_aarch64-unknown-linux-gnu.so",
"libzen_internals_x86_64-apple-darwin.dylib",
"libzen_internals_x86_64-pc-windows-gnu.dll",
"libzen_internals_x86_64-unknown-linux-gnu.so",
]


def compute_sha256(file_path):
"""Compute SHA256 digest of a file."""
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()


def download_file(url, dest_path):
"""Download a file from URL to destination path."""
print(f"Downloading {dest_path.name}...")
try:
urllib.request.urlretrieve(url, dest_path)
return True
except Exception as e:
print(f"ERROR: Failed to download {url}: {e}", file=sys.stderr)
return False


def main():
# Create temporary directory for downloads
temp_dir = Path(".cache/binaries_temp")
temp_dir.mkdir(parents=True, exist_ok=True)

print(f"Downloading binaries from {BASE_URL}")
print("=" * 70)

digests = []
upstream_checksums = {}

for binary_file in BINARY_FILES:
# Download binary
url = f"{BASE_URL}/{binary_file}"
dest_path = temp_dir / binary_file

if not download_file(url, dest_path):
print("\nERROR: Failed to download all binaries", file=sys.stderr)
sys.exit(1)

# Compute digest
digest = compute_sha256(dest_path)
digests.append((digest, binary_file))
print(f" Computed SHA256: {digest}")

# Try to download upstream checksum for comparison
checksum_url = f"{BASE_URL}/{binary_file}.sha256sum"
checksum_path = temp_dir / f"{binary_file}.sha256sum"
if download_file(checksum_url, checksum_path):
try:
with open(checksum_path, "r", encoding="utf-8") as f:
upstream_checksum = f.read().strip().split()[0]
upstream_checksums[binary_file] = upstream_checksum
print(f" Upstream SHA256: {upstream_checksum}")
if digest == upstream_checksum:
print(" ✓ Checksums match")
else:
print(" ✗ WARNING: Checksums DO NOT match!")
except Exception as e:
print(f" Warning: Could not read upstream checksum: {e}")
print()

print("=" * 70)
print("Writing digests to binary_digests.txt")

# Write digest file
digest_file = Path("binary_digests.txt")
with open(digest_file, "w", encoding="utf-8") as f:
f.write("# Trusted SHA256 digests for zen-internals v0.1.60 binaries\n")
f.write("# Format: <sha256> <filename>\n")
f.write(
"# These digests are independently verified and committed to the repository\n"
)
f.write("# Any mismatch will cause the build to fail\n")
f.write("\n")
f.write(
"# When upgrading zen-internals version:\n"
"# 1. Update BASE_URL in Makefile and this script to point to the new release\n"
"# 2. Verify the release authenticity (GPG signature, release notes, security advisory)\n"
"# 3. Run: python3 scripts/compute_binary_digests.py\n"
"# 4. Review and commit the updated digests\n"
)
f.write("\n")

for digest, filename in digests:
f.write(f"{digest} {filename}\n")

print(f"\nDigests written to {digest_file}")
print("\nIMPORTANT SECURITY VERIFICATION STEPS:")
print("1. Review the digests above")
print("2. Verify the upstream release is authentic:")
print(
f" - Check release notes at: https://github.com/AikidoSec/zen-internals/releases/tag/v0.1.60"
)
print(" - Verify GPG signatures if available")
print(" - Check for security advisories")
print("3. If upstream checksums matched, this provides some assurance")
print(
"4. Consider additional verification methods (code review, reproducible builds)"
)
print("5. Commit the digest file with a clear audit trail")

# Clean up
import shutil

shutil.rmtree(temp_dir)


if __name__ == "__main__":
main()
Loading
Loading