getproviders: Prepare for having multiple valid hashes per package
As we continue iterating towards saving valid hashes for a package in a depsfile lock file after installation and verifying them on future installation, this prepares getproviders for the possibility of having multiple valid hashes per package. This will arise in future commits for two reasons: - We will need to support both the legacy "zip hash" hashing scheme and the new-style content-based hashing scheme because currently the registry protocol is only able to produce the legacy scheme, but our other installation sources prefer the content-based scheme. Therefore packages will typically have a mixture of hashes of both types. - Installing from an upstream registry will save the hashes for the packages across all supported platforms, rather than just the current platform, and we'll consider all of those valid for future installation if we see both successful matching of the current platform checksum and a signature verification for the checksums file as a whole. This also includes some more preparation for the second case above in that signatureAuthentication now supports AcceptableHashes and returns all of the zip-based hashes it can find in the checksums file. This is a bit of an abstraction leak because previously that authenticator considered its "document" to just be opaque bytes, but we want to make sure that we can only end up trusting _all_ of the hashes if we've verified that the document is signed. Hopefully we'll make this better in a future commit with some refactoring, but that's deferred for now in order to minimize disruption to existing codepaths while we work towards a provider locking MVP.
This commit is contained in:
parent
6694cfaa0e
commit
ef64df950c
|
@ -186,11 +186,77 @@ func PackageMatchesHash(loc PackageLocation, want Hash) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
return got == want, nil
|
||||
case HashSchemeZip:
|
||||
archiveLoc, ok := loc.(PackageLocalArchive)
|
||||
if !ok {
|
||||
return false, fmt.Errorf(`ziphash scheme ("zh:" prefix) is not supported for unpacked provider packages`)
|
||||
}
|
||||
got, err := PackageHashLegacyZipSHA(archiveLoc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return got == want, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unsupported hash format (this may require a newer version of Terraform)")
|
||||
}
|
||||
}
|
||||
|
||||
// PackageMatchesAnyHash returns true if the package at the given location
|
||||
// matches at least one of the given hashes, or false otherwise.
|
||||
//
|
||||
// If it cannot read from the given location, PackageMatchesAnyHash returns an
|
||||
// error. Unlike the singular PackageMatchesHash, PackageMatchesAnyHash
|
||||
// considers unsupported hash formats as successfully non-matching, rather
|
||||
// than returning an error.
|
||||
//
|
||||
// PackageMatchesAnyHash can be used only with the two local package location
|
||||
// types PackageLocalDir and PackageLocalArchive, because it needs to access the
|
||||
// contents of the indicated package in order to compute the hash. If given
|
||||
// a non-local location this function will always return an error.
|
||||
func PackageMatchesAnyHash(loc PackageLocation, allowed []Hash) (bool, error) {
|
||||
// It's likely that we'll have multiple hashes of the same scheme in
|
||||
// the "allowed" set, in which case we'll avoid repeatedly re-reading the
|
||||
// given package by caching its result for each of the two
|
||||
// currently-supported hash formats. These will be NilHash until we
|
||||
// encounter the first hash of the corresponding scheme.
|
||||
var v1Hash, zipHash Hash
|
||||
for _, want := range allowed {
|
||||
switch want.Scheme() {
|
||||
case HashScheme1:
|
||||
if v1Hash == NilHash {
|
||||
got, err := PackageHashV1(loc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
v1Hash = got
|
||||
}
|
||||
if v1Hash == want {
|
||||
return true, nil
|
||||
}
|
||||
case HashSchemeZip:
|
||||
archiveLoc, ok := loc.(PackageLocalArchive)
|
||||
if !ok {
|
||||
// A zip hash can never match an unpacked directory
|
||||
continue
|
||||
}
|
||||
if zipHash == NilHash {
|
||||
got, err := PackageHashLegacyZipSHA(archiveLoc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
zipHash = got
|
||||
}
|
||||
if zipHash == want {
|
||||
return true, nil
|
||||
}
|
||||
default:
|
||||
// If it's not a supported format then it can't match.
|
||||
continue
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// PreferredHashes examines all of the given hash strings and returns the one
|
||||
// that the current version of Terraform considers to provide the strongest
|
||||
// verification.
|
||||
|
@ -200,10 +266,20 @@ func PackageMatchesHash(loc PackageLocation, want Hash) (bool, error) {
|
|||
// of the hash strings in "given", and that hash is the one that must pass
|
||||
// verification in order for a package to be considered valid.
|
||||
func PreferredHashes(given []Hash) []Hash {
|
||||
// For now this is just filtering for the two hash formats we support,
|
||||
// both of which are considered equally "preferred". If we introduce
|
||||
// a new scheme like "h2:" in future then, depending on the characteristics
|
||||
// of that new version, it might make sense to rework this function so
|
||||
// that it only returns "h1:" hashes if the input has no "h2:" hashes,
|
||||
// so that h2: is preferred when possible and h1: is only a fallback for
|
||||
// interacting with older systems that haven't been updated with the new
|
||||
// scheme yet.
|
||||
|
||||
var ret []Hash
|
||||
for _, hash := range given {
|
||||
if hash.Scheme() == HashScheme1 {
|
||||
return append(ret, hash)
|
||||
switch hash.Scheme() {
|
||||
case HashScheme1, HashSchemeZip:
|
||||
ret = append(ret, hash)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
|
|
|
@ -123,9 +123,9 @@ func TestHTTPMirrorSource(t *testing.T) {
|
|||
Filename: "terraform-provider-test_v1.0.0_tos_m68k.zip",
|
||||
Location: PackageHTTPURL(httpServer.URL + "/terraform.io/test/exists/terraform-provider-test_v1.0.0_tos_m68k.zip"),
|
||||
Authentication: packageHashAuthentication{
|
||||
RequiredHash: "h1:placeholder-hash",
|
||||
ValidHashes: []Hash{"h1:placeholder-hash", "h0:unacceptable-hash"},
|
||||
Platform: Platform{"tos", "m68k"},
|
||||
RequiredHashes: []Hash{"h1:placeholder-hash"},
|
||||
AllHashes: []Hash{"h1:placeholder-hash", "h0:unacceptable-hash"},
|
||||
Platform: Platform{"tos", "m68k"},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
|
@ -133,9 +133,7 @@ func TestHTTPMirrorSource(t *testing.T) {
|
|||
}
|
||||
|
||||
gotHashes := got.AcceptableHashes()
|
||||
wantHashes := map[Platform][]Hash{
|
||||
tosPlatform: {"h1:placeholder-hash", "h0:unacceptable-hash"},
|
||||
}
|
||||
wantHashes := []Hash{"h1:placeholder-hash", "h0:unacceptable-hash"}
|
||||
if diff := cmp.Diff(wantHashes, gotHashes); diff != "" {
|
||||
t.Errorf("wrong acceptable hashes\n%s", diff)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package getproviders
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
|
@ -123,9 +124,10 @@ type PackageAuthentication interface {
|
|||
type PackageAuthenticationHashes interface {
|
||||
PackageAuthentication
|
||||
|
||||
// AcceptableHashes returns a set of hash strings that this authenticator
|
||||
// would accept as valid, grouped by platform. The order of the items
|
||||
// in each of the slices is not significant, and may contain duplicates
|
||||
// AcceptableHashes returns a set of hashes that this authenticator
|
||||
// considers to be valid for the current package or, where possible,
|
||||
// equivalent packages on other platforms. The order of the items in
|
||||
// the result is not significant, and it may contain duplicates
|
||||
// that are also not significant.
|
||||
//
|
||||
// This method's result should only be used to create a "lock" for a
|
||||
|
@ -138,16 +140,17 @@ type PackageAuthenticationHashes interface {
|
|||
// verifying a signature from the origin registry, depending on what the
|
||||
// hashes are going to be used for.
|
||||
//
|
||||
// Hashes are returned as strings with hashing scheme prefixes like "h1:"
|
||||
// to record which hashing scheme the hash was created with.
|
||||
// Implementations of PackageAuthenticationHashes may return multiple
|
||||
// hashes with different schemes, which means that all of them are equally
|
||||
// acceptable.
|
||||
// acceptable. Implementors may also return hashes that use schemes the
|
||||
// current version of the authenticator would not allow but that could be
|
||||
// accepted by other versions of Terraform, e.g. if a particular hash
|
||||
// scheme has been deprecated.
|
||||
//
|
||||
// Authenticators that don't use hashes as their authentication procedure
|
||||
// will either not implement this interface or will have an implementation
|
||||
// that returns an empty result.
|
||||
AcceptableHashes() map[Platform][]Hash
|
||||
AcceptableHashes() []Hash
|
||||
}
|
||||
|
||||
type packageAuthenticationAll []PackageAuthentication
|
||||
|
@ -183,7 +186,7 @@ func (checks packageAuthenticationAll) AuthenticatePackage(localLocation Package
|
|||
return authResult, nil
|
||||
}
|
||||
|
||||
func (checks packageAuthenticationAll) AcceptableHashes() map[Platform][]Hash {
|
||||
func (checks packageAuthenticationAll) AcceptableHashes() []Hash {
|
||||
// The elements of checks are expected to be ordered so that the strongest
|
||||
// one is later in the list, so we'll visit them in reverse order and
|
||||
// take the first one that implements the interface and returns a non-empty
|
||||
|
@ -202,42 +205,37 @@ func (checks packageAuthenticationAll) AcceptableHashes() map[Platform][]Hash {
|
|||
}
|
||||
|
||||
type packageHashAuthentication struct {
|
||||
RequiredHash Hash
|
||||
ValidHashes []Hash
|
||||
Platform Platform
|
||||
RequiredHashes []Hash
|
||||
AllHashes []Hash
|
||||
Platform Platform
|
||||
}
|
||||
|
||||
// NewPackageHashAuthentication returns a PackageAuthentication implementation
|
||||
// that checks whether the contents of the package match whichever of the
|
||||
// given hashes is most preferred by the current version of Terraform.
|
||||
// that checks whether the contents of the package match whatever subset of the
|
||||
// given hashes are considered acceptable by the current version of Terraform.
|
||||
//
|
||||
// This uses the hash algorithms implemented by functions Hash and MatchesHash.
|
||||
// The PreferredHash function will select which of the given hashes is
|
||||
// considered by Terraform to be the strongest verification, and authentication
|
||||
// succeeds as long as that chosen hash matches.
|
||||
// This uses the hash algorithms implemented by functions PackageHash and
|
||||
// MatchesHash. The PreferredHashes function will select which of the given
|
||||
// hashes are considered by Terraform to be the strongest verification, and
|
||||
// authentication succeeds as long as one of those matches.
|
||||
func NewPackageHashAuthentication(platform Platform, validHashes []Hash) PackageAuthentication {
|
||||
requiredHashes := PreferredHashes(validHashes)
|
||||
// TODO: Update to support multiple hashes
|
||||
var requiredHash Hash
|
||||
if len(requiredHashes) > 0 {
|
||||
requiredHash = requiredHashes[0]
|
||||
}
|
||||
return packageHashAuthentication{
|
||||
RequiredHash: requiredHash,
|
||||
ValidHashes: validHashes,
|
||||
Platform: platform,
|
||||
RequiredHashes: requiredHashes,
|
||||
AllHashes: validHashes,
|
||||
Platform: platform,
|
||||
}
|
||||
}
|
||||
|
||||
func (a packageHashAuthentication) AuthenticatePackage(localLocation PackageLocation) (*PackageAuthenticationResult, error) {
|
||||
if a.RequiredHash == "" {
|
||||
if len(a.RequiredHashes) == 0 {
|
||||
// Indicates that none of the hashes given to
|
||||
// NewPackageHashAuthentication were considered to be usable by this
|
||||
// version of Terraform.
|
||||
return nil, fmt.Errorf("this version of Terraform does not support any of the checksum formats given for this provider")
|
||||
}
|
||||
|
||||
matches, err := PackageMatchesHash(localLocation, a.RequiredHash)
|
||||
matches, err := PackageMatchesAnyHash(localLocation, a.RequiredHashes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to verify provider package checksums: %s", err)
|
||||
}
|
||||
|
@ -245,13 +243,25 @@ func (a packageHashAuthentication) AuthenticatePackage(localLocation PackageLoca
|
|||
if matches {
|
||||
return &PackageAuthenticationResult{result: verifiedChecksum}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("provider package doesn't match the expected checksum %q", a.RequiredHash)
|
||||
if len(a.RequiredHashes) == 1 {
|
||||
return nil, fmt.Errorf("provider package doesn't match the expected checksum %q", a.RequiredHashes[0].String())
|
||||
}
|
||||
// It's non-ideal that this doesn't actually list the expected checksums,
|
||||
// but in the many-checksum case the message would get pretty unweildy.
|
||||
// In practice today we typically use this authenticator only with a
|
||||
// single hash returned from a network mirror, so the better message
|
||||
// above will prevail in that case. Maybe we'll improve on this somehow
|
||||
// if the future introduction of a new hash scheme causes there to more
|
||||
// commonly be multiple hashes.
|
||||
return nil, fmt.Errorf("provider package doesn't match the any of the expected checksums")
|
||||
}
|
||||
|
||||
func (a packageHashAuthentication) AcceptableHashes() map[Platform][]Hash {
|
||||
return map[Platform][]Hash{
|
||||
a.Platform: a.ValidHashes,
|
||||
}
|
||||
func (a packageHashAuthentication) AcceptableHashes() []Hash {
|
||||
// In this case we include even hashes the current version of Terraform
|
||||
// doesn't prefer, because this result is used for building a lock file
|
||||
// and so it's helpful to include older hash formats that other Terraform
|
||||
// versions might need in order to do authentication successfully.
|
||||
return a.AllHashes
|
||||
}
|
||||
|
||||
type archiveHashAuthentication struct {
|
||||
|
@ -270,7 +280,7 @@ type archiveHashAuthentication struct {
|
|||
// given localLocation is not PackageLocalArchive.
|
||||
//
|
||||
// NewPackageHashAuthentication is preferable to use when possible because
|
||||
// it uses the newer hashing scheme (implemented by function Hash) that
|
||||
// it uses the newer hashing scheme (implemented by function PackageHash) that
|
||||
// can work with both packed and unpacked provider packages.
|
||||
func NewArchiveChecksumAuthentication(platform Platform, wantSHA256Sum [sha256.Size]byte) PackageAuthentication {
|
||||
return archiveHashAuthentication{platform, wantSHA256Sum}
|
||||
|
@ -295,10 +305,8 @@ func (a archiveHashAuthentication) AuthenticatePackage(localLocation PackageLoca
|
|||
return &PackageAuthenticationResult{result: verifiedChecksum}, nil
|
||||
}
|
||||
|
||||
func (a archiveHashAuthentication) AcceptableHashes() map[Platform][]Hash {
|
||||
return map[Platform][]Hash{
|
||||
a.Platform: {HashLegacyZipSHAFromSHA(a.WantSHA256Sum)},
|
||||
}
|
||||
func (a archiveHashAuthentication) AcceptableHashes() []Hash {
|
||||
return []Hash{HashLegacyZipSHAFromSHA(a.WantSHA256Sum)}
|
||||
}
|
||||
|
||||
type matchingChecksumAuthentication struct {
|
||||
|
@ -437,6 +445,51 @@ func (s signatureAuthentication) AuthenticatePackage(location PackageLocation) (
|
|||
return &PackageAuthenticationResult{result: communityProvider, KeyID: keyID}, nil
|
||||
}
|
||||
|
||||
func (s signatureAuthentication) AcceptableHashes() []Hash {
|
||||
// This is a bit of an abstraction leak because signatureAuthentication
|
||||
// otherwise just treats the document as an opaque blob that's been
|
||||
// signed, but here we're making assumptions about its format because
|
||||
// we only want to trust that _all_ of the checksums are valid (rather
|
||||
// than just the current platform's one) if we've also verified that the
|
||||
// bag of checksums is signed.
|
||||
//
|
||||
// In recognition of that layering quirk this implementation is intended to
|
||||
// be somewhat resilient to potentially using this authenticator with
|
||||
// non-checksums files in future (in which case it'll return nothing at all)
|
||||
// but it might be better in the long run to instead combine
|
||||
// signatureAuthentication and matchingChecksumAuthentication together and
|
||||
// be explicit that the resulting merged authenticator is exclusively for
|
||||
// checksums files.
|
||||
|
||||
var ret []Hash
|
||||
sc := bufio.NewScanner(bytes.NewReader(s.Document))
|
||||
for sc.Scan() {
|
||||
parts := bytes.Fields(sc.Bytes())
|
||||
if len(parts) != 0 && len(parts) < 2 {
|
||||
// Doesn't look like a valid sums file line, so we'll assume
|
||||
// this whole thing isn't a checksums file.
|
||||
return nil
|
||||
}
|
||||
|
||||
// If this is a checksums file then the first part should be a
|
||||
// hex-encoded SHA256 hash, so it should be 64 characters long
|
||||
// and contain only hex digits.
|
||||
hashStr := parts[0]
|
||||
if len(hashStr) != 64 {
|
||||
return nil // doesn't look like a checksums file
|
||||
}
|
||||
|
||||
var gotSHA256Sum [sha256.Size]byte
|
||||
if _, err := hex.Decode(gotSHA256Sum[:], hashStr); err != nil {
|
||||
return nil // doesn't look like a checksums file
|
||||
}
|
||||
|
||||
ret = append(ret, HashLegacyZipSHAFromSHA(gotSHA256Sum))
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// findSigningKey attempts to verify the signature using each of the keys
|
||||
// returned by the registry. If a valid signature is found, it returns the
|
||||
// signing key.
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/crypto/openpgp"
|
||||
)
|
||||
|
||||
|
@ -388,7 +389,7 @@ func TestSignatureAuthentication_success(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
auth := NewSignatureAuthentication([]byte(testShaSums), signature, test.keys)
|
||||
auth := NewSignatureAuthentication([]byte(testShaSumsPlaceholder), signature, test.keys)
|
||||
result, err := auth.AuthenticatePackage(location)
|
||||
|
||||
if result == nil || *result != test.result {
|
||||
|
@ -468,7 +469,7 @@ func TestSignatureAuthentication_failure(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
auth := NewSignatureAuthentication([]byte(testShaSums), signature, test.keys)
|
||||
auth := NewSignatureAuthentication([]byte(testShaSumsPlaceholder), signature, test.keys)
|
||||
result, err := auth.AuthenticatePackage(location)
|
||||
|
||||
if result != nil {
|
||||
|
@ -481,6 +482,33 @@ func TestSignatureAuthentication_failure(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSignatureAuthentication_acceptableHashes(t *testing.T) {
|
||||
auth := NewSignatureAuthentication([]byte(testShaSumsRealistic), nil, nil)
|
||||
authWithHashes, ok := auth.(PackageAuthenticationHashes)
|
||||
if !ok {
|
||||
t.Fatalf("%T does not implement PackageAuthenticationHashes", auth)
|
||||
}
|
||||
got := authWithHashes.AcceptableHashes()
|
||||
want := []Hash{
|
||||
// These are the hashes encoded in constant testShaSumsRealistic
|
||||
"zh:7d7e888fdd28abfe00894f9055209b9eec785153641de98e6852aa071008d4ee",
|
||||
"zh:f8b6cf9ade087c17826d49d89cef21261cdc22bd27065bbc5b27d7dbf7fbbf6c",
|
||||
"zh:a5ba9945606bb7bfb821ba303957eeb40dd9ee4e706ba8da1eaf7cbeb0356e63",
|
||||
"zh:df3a5a8d6ffff7bacf19c92d10d0d500f98169ea17b3764b01a789f563d1aad7",
|
||||
"zh:086119a26576d06b8281a97e8644380da89ce16197cd955f74ea5ee664e9358b",
|
||||
"zh:1e5f7a5f3ade7b8b1d1d59c5cea2e1a2f8d2f8c3f41962dbbe8647e222be8239",
|
||||
"zh:0e9fd0f3e2254b526a0e81e0cfdfc82583b0cd343778c53ead21aa7d52f776d7",
|
||||
"zh:66a947e7de1c74caf9f584c3ed4e91d2cb1af6fe5ce8abaf1cf8f7ff626a09d1",
|
||||
"zh:def1b73849bec0dc57a04405847921bf9206c75b52ae9de195476facb26bd85e",
|
||||
"zh:48f1826ec31d6f104e46cc2022b41f30cd1019ef48eaec9697654ef9ec37a879",
|
||||
"zh:17e0b496022bc4e4137be15e96d2b051c8acd6e14cb48d9b13b262330464f6cc",
|
||||
"zh:2696c86228f491bc5425561c45904c9ce39b1c676b1e17734cb2ee6b578c4bcd",
|
||||
}
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("wrong result\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
const testAuthorKeyID = `37A6AB3BCF2C170A`
|
||||
|
||||
// testAuthorKeyArmor is test key ID 5BFEEC4317E746008621970637A6AB3BCF2C170A.
|
||||
|
@ -554,9 +582,26 @@ SnHodBLlpKLyUXi36DCDy/iKVsieqLsAdcYe0nQFuhoQcOme33A=
|
|||
=aHOG
|
||||
-----END PGP SIGNATURE-----`
|
||||
|
||||
// testShaSums is a string that represents the SHA256SUMS file downloaded
|
||||
// for a release.
|
||||
const testShaSums = "example shasums data"
|
||||
// testShaSumsPlaceholder is a string that represents a signed document that
|
||||
// the signature authenticator will check. Some of the signature valuesin
|
||||
// other constants in this file are signing this string.
|
||||
const testShaSumsPlaceholder = "example shasums data"
|
||||
|
||||
// testShaSumsRealistic is a more realistic SHA256SUMS document that we can use
|
||||
// to test the AcceptableHashes method. The signature values in other constants
|
||||
// in this file do not sign this string.
|
||||
const testShaSumsRealistic = `7d7e888fdd28abfe00894f9055209b9eec785153641de98e6852aa071008d4ee terraform_0.14.0-alpha20200923_darwin_amd64.zip
|
||||
f8b6cf9ade087c17826d49d89cef21261cdc22bd27065bbc5b27d7dbf7fbbf6c terraform_0.14.0-alpha20200923_freebsd_386.zip
|
||||
a5ba9945606bb7bfb821ba303957eeb40dd9ee4e706ba8da1eaf7cbeb0356e63 terraform_0.14.0-alpha20200923_freebsd_amd64.zip
|
||||
df3a5a8d6ffff7bacf19c92d10d0d500f98169ea17b3764b01a789f563d1aad7 terraform_0.14.0-alpha20200923_freebsd_arm.zip
|
||||
086119a26576d06b8281a97e8644380da89ce16197cd955f74ea5ee664e9358b terraform_0.14.0-alpha20200923_linux_386.zip
|
||||
1e5f7a5f3ade7b8b1d1d59c5cea2e1a2f8d2f8c3f41962dbbe8647e222be8239 terraform_0.14.0-alpha20200923_linux_amd64.zip
|
||||
0e9fd0f3e2254b526a0e81e0cfdfc82583b0cd343778c53ead21aa7d52f776d7 terraform_0.14.0-alpha20200923_linux_arm.zip
|
||||
66a947e7de1c74caf9f584c3ed4e91d2cb1af6fe5ce8abaf1cf8f7ff626a09d1 terraform_0.14.0-alpha20200923_openbsd_386.zip
|
||||
def1b73849bec0dc57a04405847921bf9206c75b52ae9de195476facb26bd85e terraform_0.14.0-alpha20200923_openbsd_amd64.zip
|
||||
48f1826ec31d6f104e46cc2022b41f30cd1019ef48eaec9697654ef9ec37a879 terraform_0.14.0-alpha20200923_solaris_amd64.zip
|
||||
17e0b496022bc4e4137be15e96d2b051c8acd6e14cb48d9b13b262330464f6cc terraform_0.14.0-alpha20200923_windows_386.zip
|
||||
2696c86228f491bc5425561c45904c9ce39b1c676b1e17734cb2ee6b578c4bcd terraform_0.14.0-alpha20200923_windows_amd64.zip`
|
||||
|
||||
// testAuthorSignatureGoodBase64 is a signature of testShaSums signed with
|
||||
// testAuthorKeyArmor, which represents the SHA256SUMS.sig file downloaded for
|
||||
|
|
|
@ -174,7 +174,7 @@ func fakeRegistryHandler(resp http.ResponseWriter, req *http.Request) {
|
|||
case "/pkg/awesomesauce/happycloud_1.2.0.zip":
|
||||
resp.Write([]byte("some zip file"))
|
||||
case "/pkg/awesomesauce/happycloud_1.2.0_SHA256SUMS":
|
||||
resp.Write([]byte("000000000000000000000000000000000000000000000000000000000000f00d happycloud_1.2.0.zip\n"))
|
||||
resp.Write([]byte("000000000000000000000000000000000000000000000000000000000000f00d happycloud_1.2.0.zip\n000000000000000000000000000000000000000000000000000000000000face happycloud_1.2.0_face.zip\n"))
|
||||
case "/pkg/awesomesauce/happycloud_1.2.0_SHA256SUMS.sig":
|
||||
resp.Write([]byte("GPG signature"))
|
||||
default:
|
||||
|
|
|
@ -115,11 +115,11 @@ func TestSourcePackageMeta(t *testing.T) {
|
|||
version string
|
||||
os, arch string
|
||||
want PackageMeta
|
||||
wantHashes map[Platform][]Hash
|
||||
wantHashes []Hash
|
||||
wantErr string
|
||||
}{
|
||||
// These test cases are relying on behaviors of the fake provider
|
||||
// registry server implemented in client_test.go.
|
||||
// registry server implemented in registry_client_test.go.
|
||||
{
|
||||
"example.com/awesomesauce/happycloud",
|
||||
"1.2.0",
|
||||
|
@ -135,13 +135,13 @@ func TestSourcePackageMeta(t *testing.T) {
|
|||
Location: PackageHTTPURL(baseURL + "/pkg/awesomesauce/happycloud_1.2.0.zip"),
|
||||
Authentication: PackageAuthenticationAll(
|
||||
NewMatchingChecksumAuthentication(
|
||||
[]byte("000000000000000000000000000000000000000000000000000000000000f00d happycloud_1.2.0.zip\n"),
|
||||
[]byte("000000000000000000000000000000000000000000000000000000000000f00d happycloud_1.2.0.zip\n000000000000000000000000000000000000000000000000000000000000face happycloud_1.2.0_face.zip\n"),
|
||||
"happycloud_1.2.0.zip",
|
||||
[32]byte{30: 0xf0, 31: 0x0d},
|
||||
),
|
||||
NewArchiveChecksumAuthentication(Platform{"linux", "amd64"}, [32]byte{30: 0xf0, 31: 0x0d}),
|
||||
NewSignatureAuthentication(
|
||||
[]byte("000000000000000000000000000000000000000000000000000000000000f00d happycloud_1.2.0.zip\n"),
|
||||
[]byte("000000000000000000000000000000000000000000000000000000000000f00d happycloud_1.2.0.zip\n000000000000000000000000000000000000000000000000000000000000face happycloud_1.2.0_face.zip\n"),
|
||||
[]byte("GPG signature"),
|
||||
[]SigningKey{
|
||||
{ASCIIArmor: HashicorpPublicKey},
|
||||
|
@ -149,10 +149,9 @@ func TestSourcePackageMeta(t *testing.T) {
|
|||
),
|
||||
),
|
||||
},
|
||||
map[Platform][]Hash{
|
||||
{"linux", "amd64"}: {
|
||||
"zh:000000000000000000000000000000000000000000000000000000000000f00d",
|
||||
},
|
||||
[]Hash{
|
||||
"zh:000000000000000000000000000000000000000000000000000000000000f00d",
|
||||
"zh:000000000000000000000000000000000000000000000000000000000000face",
|
||||
},
|
||||
``,
|
||||
},
|
||||
|
|
|
@ -244,9 +244,19 @@ func (m PackageMeta) PackedFilePath(baseDir string) string {
|
|||
return PackedFilePathForPackage(baseDir, m.Provider, m.Version, m.TargetPlatform)
|
||||
}
|
||||
|
||||
// AcceptableHashes returns a set of hashes (grouped by target platform) that
|
||||
// could be recorded for comparison to future results for the same provider
|
||||
// version, to implement a "trust on first use" scheme.
|
||||
// AcceptableHashes returns a set of hashes that could be recorded for
|
||||
// comparison to future results for the same provider version, to implement a
|
||||
// "trust on first use" scheme.
|
||||
//
|
||||
// The AcceptableHashes result is a platform-agnostic set of hashes, with the
|
||||
// intent that in most cases it will be used as an additional cross-check in
|
||||
// addition to a platform-specific hash check made during installation. However,
|
||||
// there are some situations (such as verifying an already-installed package
|
||||
// that's on local disk) where Terraform would check only against the results
|
||||
// of this function, meaning that it would in principle accept another
|
||||
// platform's package as a substitute for the correct platform. That's a
|
||||
// pragmatic compromise to allow lock files derived from the result of this
|
||||
// method to be portable across platforms.
|
||||
//
|
||||
// Callers of this method should typically also verify the package using the
|
||||
// object in the Authentication field, and consider how much trust to give
|
||||
|
@ -259,7 +269,7 @@ func (m PackageMeta) PackedFilePath(baseDir string) string {
|
|||
// Authentication field. AcceptableHashes therefore returns an empty result
|
||||
// for a PackageMeta that has no authentication object, or has one that does
|
||||
// not make use of hashes.
|
||||
func (m PackageMeta) AcceptableHashes() map[Platform][]Hash {
|
||||
func (m PackageMeta) AcceptableHashes() []Hash {
|
||||
auth, ok := m.Authentication.(PackageAuthenticationHashes)
|
||||
if !ok {
|
||||
return nil
|
||||
|
|
Loading…
Reference in New Issue