// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 package providercache import ( "archive/zip" "bytes" "crypto/sha256" "fmt" "io" "os" "path/filepath" "strings" "testing" "github.com/ProtonMail/go-crypto/openpgp" "github.com/ProtonMail/go-crypto/openpgp/armor" ) type providerPkg struct { zipContent []byte shaSumContent []byte sigContent []byte key *bytes.Buffer keyID string filePrefix string // _ } // createTestProvider creates a test provider package with required verification files func createTestProvider(t *testing.T, providerName, version string) *providerPkg { entity, pub := generateGPGKey(t) dir := t.TempDir() fileName := fmt.Sprintf("%s_%s", providerName, version) // Create a zip file for the provider zipFile := filepath.Join(dir, fileName) zipContent := createProviderZip(t, zipFile, fileName) // Generate SHA256SUMS file sumFile, sumContent, err := createSHASums(dir, fileName) if err != nil { t.Fatalf("failed to create SHA sums: %s", err) } // Sign the SHA256SUMS file sigContent, err := createDetachedSignature(sumFile, entity) if err != nil { t.Fatalf("failed to create signature: %s", err) } return &providerPkg{ zipContent: zipContent, shaSumContent: sumContent, sigContent: sigContent, key: pub, keyID: entity.PrimaryKey.KeyIdString(), filePrefix: fileName, } } func generateGPGKey(t *testing.T) (*openpgp.Entity, *bytes.Buffer) { entity, err := openpgp.NewEntity("Terraform Test", "test", "terraform@example.com", nil) if err != nil { t.Fatalf("failed to create entity: %s", err) } // Export the public key in armored format pubBuf := bytes.NewBuffer(nil) w, err := armor.Encode(pubBuf, openpgp.PublicKeyType, nil) if err != nil { t.Fatalf("failed to create armor writer: %s", err) } if err := entity.Serialize(w); err != nil { t.Fatalf("failed to serialize key: %s", err) } if err := w.Close(); err != nil { t.Fatalf("failed to finalize armor: %s", err) } return entity, pubBuf } func createProviderZip(t *testing.T, zipPath, filename string) []byte { zipBuffer := new(bytes.Buffer) zipWriter := zip.NewWriter(zipBuffer) // Add file to the archive _, err := zipWriter.Create(fmt.Sprintf("terraform-provider-%s", filename)) if err != nil { t.Fatalf("failed to create zip entry: %s", err) } if err := zipWriter.Close(); err != nil { t.Fatalf("failed to close zip writer: %s", err) } // Write the zip to a file zipContent := zipBuffer.Bytes() if err := os.WriteFile(zipPath+".zip", zipContent, 0666); err != nil { t.Fatalf("failed to write zip file: %s", err) } return zipContent } // createDetachedSignature creates a PGP detached signature for the given file func createDetachedSignature(filePath string, entity *openpgp.Entity) ([]byte, error) { r, err := os.Open(filePath) if err != nil { return nil, err } defer safeClose(r) sigPath := filePath + ".sig" w, err := os.Create(sigPath) if err != nil { return nil, err } defer safeClose(w) if err := openpgp.DetachSign(w, entity, r, nil); err != nil { return nil, err } // Read the signature back return os.ReadFile(sigPath) } // createSHASums creates SHA256 sums for all files in the directory func createSHASums(dir, name string) (string, []byte, error) { var sums []string files, err := os.ReadDir(dir) if err != nil { return "", nil, err } for _, file := range files { if file.IsDir() { continue } filePath := filepath.Join(dir, file.Name()) sum, err := calculateFileHash(filePath) if err != nil { return "", nil, err } sums = append(sums, fmt.Sprintf("%x %s", sum, file.Name())) } sumContent := []byte(strings.Join(sums, "\n") + "\n") sumPath := filepath.Join(dir, fmt.Sprintf("%s_SHA256SUMS", name)) if err := os.WriteFile(sumPath, sumContent, 0666); err != nil { return "", nil, err } return sumPath, sumContent, nil } // calculateFileHash returns the SHA256 hash of a file func calculateFileHash(path string) ([]byte, error) { f, err := os.Open(path) if err != nil { return nil, err } defer safeClose(f) h := sha256.New() if _, err := io.Copy(h, f); err != nil { return nil, err } return h.Sum(nil), nil } // safeClose safely closes a file, logging any errors func safeClose(f *os.File) { if err := f.Close(); err != nil { fmt.Printf("failed to close file: %s\n", err) } }