Skip to content

Go Edition: Download, PGP verification, SHA256 verification & extraction #496

@Zordrak

Description

@Zordrak

Summary

Implement the download, cryptographic verification (PGP signatures + SHA256 checksums), and zip extraction pipeline for Terraform binaries.

Parent Epic

Part of #488 — Go Edition: Full Feature Parity Implementation

Motivation

This is the security-critical component that downloads Terraform binaries from HashiCorp's release infrastructure, verifies their authenticity via PGP signatures and integrity via SHA256 checksums, and extracts them for installation. The Go edition embeds the HashiCorp PGP key at compile time, eliminating the Bash edition's fragile 3-tier verification fallback (keybase → gpgv → gpg).

Clean-Room Constraint

This is a clean-room implementation. Contributors MUST NOT read, reference, copy, or adapt source code from tofuutils/tenv, hashicorp/hc-install, or any other third-party tfenv-like tool. The sole reference is tfenv's own Bash source code, documentation, and test suite.

Proposed Design

Package Location

go/internal/install/ (download and verification components)

Download Pipeline

For a given version and platform:

  1. Download SHA256SUMS: ${TFENV_REMOTE}/terraform/${version}/terraform_${version}_SHA256SUMS
  2. Download SHA256SUMS.sig: ${TFENV_REMOTE}/terraform/${version}/terraform_${version}_SHA256SUMS.sig
  3. Verify PGP signature: Verify SHA256SUMS.sig against SHA256SUMS using the HashiCorp PGP public key
  4. Download zip archive: ${TFENV_REMOTE}/terraform/${version}/terraform_${version}_${os}_${arch}.zip
  5. Verify SHA256 checksum: Hash the downloaded zip and compare against the entry in SHA256SUMS
  6. Extract: Unzip to ${TFENV_CONFIG_DIR}/versions/${version}/

URL and Filename Conventions

Standard tarball naming:

terraform_${version}_${os}_${arch}.zip

0.12.0-alpha edge case: Versions 0.12.0-alpha3 through 0.12.0-alpha9 use a double-prefixed filename:

terraform_${version}_terraform_${version}_${os}_${arch}.zip

Reference: libexec/tfenv-install line 194.

SHA256SUMS file:

terraform_${version}_SHA256SUMS

Signature file naming: The signature filename includes a key ID postfix:

terraform_${version}_SHA256SUMS.72D7468F.sig

Note the .72D7468F postfix is the PGP key fingerprint suffix. Reference: libexec/tfenv-install line 199.

PGP Key Embedding

The HashiCorp PGP public key is embedded at compile time:

//go:embed ../../../share/hashicorp-keys.pgp
var hashicorpPGPKey []byte

Override mechanism: TFENV_PGP_KEY_PATH (or similar) allows users to supply a custom key for private mirrors.

PGP Verification Library

Evaluate ProtonMail/gopenpgp or golang.org/x/crypto/openpgp (deprecated but functional) for signature verification. The choice should prioritise correctness, maintenance status, and minimal dependency footprint.

SHA256 Verification

  • Parse the SHA256SUMS file to find the checksum for the target platform's zip
  • Compute SHA256 of downloaded zip using crypto/sha256
  • Compare (constant-time) against expected checksum

Zip Extraction

  • Use archive/zip stdlib to extract the terraform binary from the zip
  • Set executable permissions on the extracted binary (0755)
  • Handle single-file zips (Terraform zips contain only the terraform binary)
  • Handle Windows: extract terraform.exe
  • Security: path traversal protection. Reject zip entries with .. path components or absolute paths to prevent zip-slip attacks

Atomic Install

Downloads should go to a temporary directory first. Only after ALL verification passes (PGP + SHA256), the binary is moved to the final ${TFENV_CONFIG_DIR}/versions/${version}/ location. This prevents partially-installed or unverified binaries from being visible.

Bash Edition Verification Modes (for reference)

The Bash edition has a 3-tier PGP verification fallback. The Go edition eliminates this complexity entirely:

Bash Mode Mechanism Go Edition Equivalent
use-gnupg config file User's GnuPG keyring Not supported — use embedded key
use-gpgv config file gpgv with optional tfenv trust Not supported — use embedded key
Keybase installed + following hashicorp keybase pgp verify Not supported — use embedded key
None of the above No PGP verification (SHA256 only) Always verified — key is embedded

The Go edition always verifies PGP signatures because the key is compiled into the binary. This is a security improvement over the Bash edition where most users have no PGP verification at all.

HTTP Downloads

  • net/http with configurable timeout
  • Respect proxy environment variables (HTTPS_PROXY, etc.)
  • TFENV_NETRC_PATH for authenticated mirrors
  • TFENV_CURL_OUTPUT controls download progress display (mapping to Go progress reporting)
  • User-Agent: tfenv/<version>

Error Handling

Every step must fail clearly:

  • Network errors: "Failed to download SHA256SUMS for Terraform 1.5.0: connection refused"
  • PGP verification failure: "PGP signature verification FAILED for Terraform 1.5.0 — the download may be tampered with"
  • SHA256 mismatch: "SHA256 checksum mismatch for terraform_1.5.0_linux_amd64.zip — expected abc123, got def456"
  • Extraction failure: "Failed to extract terraform binary from zip"

Acceptance Criteria

  • Downloads SHA256SUMS, SHA256SUMS.sig, and zip archive from configured remote
  • PGP signature verification of SHA256SUMS against embedded HashiCorp key
  • PGP verification fails loudly and aborts on invalid/missing signature
  • SHA256 checksum verification of downloaded zip against SHA256SUMS entry
  • SHA256 mismatch fails loudly and aborts (does not install corrupted binary)
  • Zip extraction places terraform binary in correct versions directory
  • Extracted binary has executable permissions (0755)
  • Windows: extracts terraform.exe correctly
  • Custom PGP key override via environment variable works for private mirrors
  • TFENV_REMOTE is used as the base URL for all downloads
  • TFENV_NETRC_PATH enables authenticated downloads
  • Download progress is reported when appropriate (TTY detection)
  • Unit tests verify PGP checking with a test keypair and test-signed artifacts
  • Unit tests verify SHA256 checking with known hashes
  • Unit tests verify zip extraction with a crafted test zip
  • Integration test (build-tagged) downloads a real Terraform version and verifies it
  • //go:embed is used for the HashiCorp PGP key from share/hashicorp-keys.pgp
  • 0.12.0-alpha tarball naming edge case is handled
  • SHA256SUMS.sig filename includes .72D7468F key ID postfix
  • Zip extraction rejects entries with .. path components (zip-slip protection)
  • Downloads go to temp dir first, only moved to final location after all verification passes

Dependencies

Implementation Notes

  • Reference libexec/tfenv-install for the full Bash download/verify/extract flow
  • Reference lib/helpers.sh for curlw() and the PGP/SHA256 verification logic
  • The Bash edition has a 3-tier PGP fallback (keybase → gpgv → gpg) — the Go edition eliminates this entirely with embedded key + Go crypto
  • The Bash edition uses system unzip — the Go edition uses archive/zip stdlib
  • The Bash edition uses shasum -a 256 or sha256sum — the Go edition uses crypto/sha256
  • Issue tfenv skipping signature verification despite shasum being in path #369 (skipping signature verification) and tfenv downloaded a Terraform 013.3 version that is being flagged as malware #393 (malware concern) are relevant context
  • Downloads should go to a temporary directory first, then move to final location on success (atomic install preparation for the Install command)
  • Consider supporting TFENV_SKIP_REMOTE_CHECK to skip download verification in air-gapped environments with pre-populated versions directory

Labels

type:feature, priority:high, complexity:large, category:verification

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions