internal/getproviders: Formalize the "ziphash" hashing scheme
This is the pre-existing hashing scheme that was initially built for releases.hashicorp.com and then later reused for the provider registry protocol, which takes a SHA256 hash of the official distribution .zip file and formats it as lowercase hex. This is a non-ideal hash scheme because it works only for PackageLocalArchive locations, and so we can't verify package directories on local disk against such hashes. However, the registry protocol is now a compatibility constraint and so we're going to need to support this hashing scheme for the foreseeable future.
This commit is contained in:
parent
0b632c872e
commit
e843097e52
|
@ -1,13 +1,19 @@
|
|||
package getproviders
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
)
|
||||
|
||||
const h1Prefix = "h1:"
|
||||
const zipHashPrefix = "zh:"
|
||||
|
||||
// PackageHash computes a hash of the contents of the package at the given
|
||||
// location, using whichever hash algorithm is the current default.
|
||||
//
|
||||
|
@ -40,7 +46,7 @@ func PackageHash(loc PackageLocation) (string, error) {
|
|||
// a non-local location this function will always return an error.
|
||||
func PackageMatchesHash(loc PackageLocation, want string) (bool, error) {
|
||||
switch {
|
||||
case strings.HasPrefix(want, "h1"):
|
||||
case strings.HasPrefix(want, h1Prefix):
|
||||
got, err := PackageHashV1(loc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -61,13 +67,56 @@ func PackageMatchesHash(loc PackageLocation, want string) (bool, error) {
|
|||
// verification in order for a package to be considered valid.
|
||||
func PreferredHash(given []string) string {
|
||||
for _, s := range given {
|
||||
if strings.HasPrefix(s, "h1:") {
|
||||
if strings.HasPrefix(s, h1Prefix) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// PackageHashLegacyZipSHA implements the old provider package hashing scheme
|
||||
// of taking a SHA256 hash of the containing .zip archive itself, rather than
|
||||
// of the contents of the archive.
|
||||
//
|
||||
// The result is a hash string with the "zh:" prefix, which is intended to
|
||||
// represent "zip hash". After the prefix is a lowercase-hex encoded SHA256
|
||||
// checksum, intended to exactly match the formatting used in the registry
|
||||
// API (apart from the prefix) so that checksums can be more conveniently
|
||||
// compared by humans.
|
||||
//
|
||||
// Because this hashing scheme uses the official provider .zip file as its
|
||||
// input, it accepts only PackageLocalArchive locations.
|
||||
func PackageHashLegacyZipSHA(loc PackageLocalArchive) (string, error) {
|
||||
archivePath, err := filepath.EvalSymlinks(string(loc))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
f, err := os.Open(archivePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := sha256.New()
|
||||
_, err = io.Copy(h, f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
gotHash := h.Sum(nil)
|
||||
return fmt.Sprintf("%s%x", zipHashPrefix, gotHash), nil
|
||||
}
|
||||
|
||||
// HashLegacyZipSHAFromSHA is a convenience method to produce the schemed-string
|
||||
// hash format from an already-calculated hash of a provider .zip archive.
|
||||
//
|
||||
// This just adds the "zh:" prefix and encodes the string in hex, so that the
|
||||
// result is in the same format as PackageHashLegacyZipSHA.
|
||||
func HashLegacyZipSHAFromSHA(sum [sha256.Size]byte) string {
|
||||
return fmt.Sprintf("%s%x", zipHashPrefix, sum[:])
|
||||
}
|
||||
|
||||
// PackageHashV1 computes a hash of the contents of the package at the given
|
||||
// location using hash algorithm 1.
|
||||
//
|
||||
|
|
|
@ -181,25 +181,23 @@ func (a archiveHashAuthentication) AuthenticatePackage(localLocation PackageLoca
|
|||
return nil, fmt.Errorf("cannot check archive hash for non-archive location %s", localLocation)
|
||||
}
|
||||
|
||||
f, err := os.Open(string(archiveLocation))
|
||||
gotHash, err := PackageHashLegacyZipSHA(archiveLocation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to compute checksum for %s: %s", archiveLocation, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := sha256.New()
|
||||
_, err = io.Copy(h, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gotHash := h.Sum(nil)
|
||||
if !bytes.Equal(gotHash, a.WantSHA256Sum[:]) {
|
||||
return nil, fmt.Errorf("archive has incorrect SHA-256 checksum %x (expected %x)", gotHash, a.WantSHA256Sum[:])
|
||||
wantHash := HashLegacyZipSHAFromSHA(a.WantSHA256Sum)
|
||||
if gotHash != wantHash {
|
||||
return nil, fmt.Errorf("archive has incorrect checksum %s (expected %s)", gotHash, wantHash)
|
||||
}
|
||||
return &PackageAuthenticationResult{result: verifiedChecksum}, nil
|
||||
}
|
||||
|
||||
func (a archiveHashAuthentication) AcceptableHashes() map[Platform][]string {
|
||||
return map[Platform][]string{
|
||||
a.Platform: {HashLegacyZipSHAFromSHA(a.WantSHA256Sum)},
|
||||
}
|
||||
}
|
||||
|
||||
type matchingChecksumAuthentication struct {
|
||||
Document []byte
|
||||
Filename string
|
||||
|
|
Loading…
Reference in New Issue