github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/getproviders/package_authentication_test.go (about)

     1  package getproviders
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"encoding/base64"
     6  	"errors"
     7  	"fmt"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  
    13  	// TODO: replace crypto/openpgp since it is deprecated
    14  	// https://github.com/golang/go/issues/44226
    15  	//lint:file-ignore SA1019 openpgp is deprecated but there are no good alternatives yet
    16  	"golang.org/x/crypto/openpgp"
    17  )
    18  
    19  func TestPackageAuthenticationResult(t *testing.T) {
    20  	tests := []struct {
    21  		result *PackageAuthenticationResult
    22  		want   string
    23  	}{
    24  		{
    25  			nil,
    26  			"unauthenticated",
    27  		},
    28  		{
    29  			&PackageAuthenticationResult{result: verifiedChecksum},
    30  			"verified checksum",
    31  		},
    32  		{
    33  			&PackageAuthenticationResult{result: officialProvider},
    34  			"signed by HashiCorp",
    35  		},
    36  		{
    37  			&PackageAuthenticationResult{result: partnerProvider},
    38  			"signed by a HashiCorp partner",
    39  		},
    40  		{
    41  			&PackageAuthenticationResult{result: communityProvider},
    42  			"self-signed",
    43  		},
    44  	}
    45  	for _, test := range tests {
    46  		if got := test.result.String(); got != test.want {
    47  			t.Errorf("wrong value: got %q, want %q", got, test.want)
    48  		}
    49  	}
    50  }
    51  
    52  // mockAuthentication is an implementation of the PackageAuthentication
    53  // interface which returns fixed values. This is used to test the combining
    54  // logic of PackageAuthenticationAll.
    55  type mockAuthentication struct {
    56  	result packageAuthenticationResult
    57  	err    error
    58  }
    59  
    60  func (m mockAuthentication) AuthenticatePackage(localLocation PackageLocation) (*PackageAuthenticationResult, error) {
    61  	if m.err == nil {
    62  		return &PackageAuthenticationResult{result: m.result}, nil
    63  	} else {
    64  		return nil, m.err
    65  	}
    66  }
    67  
    68  var _ PackageAuthentication = (*mockAuthentication)(nil)
    69  
    70  // If all authentications succeed, the returned result should come from the
    71  // last authentication.
    72  func TestPackageAuthenticationAll_success(t *testing.T) {
    73  	result, err := PackageAuthenticationAll(
    74  		&mockAuthentication{result: verifiedChecksum},
    75  		&mockAuthentication{result: communityProvider},
    76  	).AuthenticatePackage(nil)
    77  
    78  	want := PackageAuthenticationResult{result: communityProvider}
    79  	if result == nil || *result != want {
    80  		t.Errorf("wrong result: want %#v, got %#v", want, result)
    81  	}
    82  	if err != nil {
    83  		t.Errorf("wrong err: got %#v, want nil", err)
    84  	}
    85  }
    86  
    87  // If an authentication fails, its error should be returned along with a nil
    88  // result.
    89  func TestPackageAuthenticationAll_failure(t *testing.T) {
    90  	someError := errors.New("some error")
    91  	result, err := PackageAuthenticationAll(
    92  		&mockAuthentication{result: verifiedChecksum},
    93  		&mockAuthentication{err: someError},
    94  		&mockAuthentication{result: communityProvider},
    95  	).AuthenticatePackage(nil)
    96  
    97  	if result != nil {
    98  		t.Errorf("wrong result: got %#v, want nil", result)
    99  	}
   100  	if err != someError {
   101  		t.Errorf("wrong err: got %#v, want %#v", err, someError)
   102  	}
   103  }
   104  
   105  // Package hash authentication requires a zip file or directory fixture and a
   106  // known-good set of hashes, of which the authenticator will pick one. The
   107  // result should be "verified checksum".
   108  func TestPackageHashAuthentication_success(t *testing.T) {
   109  	// Location must be a PackageLocalArchive path
   110  	location := PackageLocalDir("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/2.0.0/linux_amd64")
   111  
   112  	wantHashes := []Hash{
   113  		// Known-good HashV1 result for this directory
   114  		Hash("h1:qjsREM4DqEWECD43FcPqddZ9oxCG+IaMTxvWPciS05g="),
   115  	}
   116  
   117  	auth := NewPackageHashAuthentication(Platform{"linux", "amd64"}, wantHashes)
   118  	result, err := auth.AuthenticatePackage(location)
   119  
   120  	wantResult := PackageAuthenticationResult{result: verifiedChecksum}
   121  	if result == nil || *result != wantResult {
   122  		t.Errorf("wrong result: got %#v, want %#v", result, wantResult)
   123  	}
   124  	if err != nil {
   125  		t.Errorf("wrong err: got %s, want nil", err)
   126  	}
   127  }
   128  
   129  // Package has authentication can fail for various reasons.
   130  func TestPackageHashAuthentication_failure(t *testing.T) {
   131  	tests := map[string]struct {
   132  		location PackageLocation
   133  		err      string
   134  	}{
   135  		"missing file": {
   136  			PackageLocalArchive("testdata/no-package-here.zip"),
   137  			"failed to verify provider package checksums: lstat testdata/no-package-here.zip: no such file or directory",
   138  		},
   139  		"checksum mismatch": {
   140  			PackageLocalDir("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/2.0.0/linux_amd64"),
   141  			"provider package doesn't match the expected checksum \"h1:invalid\"",
   142  		},
   143  		"invalid zip file": {
   144  			PackageLocalArchive("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_linux_amd64.zip"),
   145  			"failed to verify provider package checksums: zip: not a valid zip file",
   146  		},
   147  	}
   148  
   149  	for name, test := range tests {
   150  		t.Run(name, func(t *testing.T) {
   151  			// Invalid expected hash, either because we'll error before we
   152  			// reach it, or we want to force a checksum mismatch.
   153  			auth := NewPackageHashAuthentication(Platform{"linux", "amd64"}, []Hash{"h1:invalid"})
   154  			result, err := auth.AuthenticatePackage(test.location)
   155  
   156  			if result != nil {
   157  				t.Errorf("wrong result: got %#v, want nil", result)
   158  			}
   159  			if gotErr := err.Error(); gotErr != test.err {
   160  				t.Errorf("wrong err: got %q, want %q", gotErr, test.err)
   161  			}
   162  		})
   163  	}
   164  }
   165  
   166  // Archive checksum authentication requires a file fixture and a known-good
   167  // SHA256 hash. The result should be "verified checksum".
   168  func TestArchiveChecksumAuthentication_success(t *testing.T) {
   169  	// Location must be a PackageLocalArchive path
   170  	location := PackageLocalArchive("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_linux_amd64.zip")
   171  
   172  	// Known-good SHA256 hash for this archive
   173  	wantSHA256Sum := [sha256.Size]byte{
   174  		0x4f, 0xb3, 0x98, 0x49, 0xf2, 0xe1, 0x38, 0xeb,
   175  		0x16, 0xa1, 0x8b, 0xa0, 0xc6, 0x82, 0x63, 0x5d,
   176  		0x78, 0x1c, 0xb8, 0xc3, 0xb2, 0x59, 0x01, 0xdd,
   177  		0x5a, 0x79, 0x2a, 0xde, 0x97, 0x11, 0xf5, 0x01,
   178  	}
   179  
   180  	auth := NewArchiveChecksumAuthentication(Platform{"linux", "amd64"}, wantSHA256Sum)
   181  	result, err := auth.AuthenticatePackage(location)
   182  
   183  	wantResult := PackageAuthenticationResult{result: verifiedChecksum}
   184  	if result == nil || *result != wantResult {
   185  		t.Errorf("wrong result: got %#v, want %#v", result, wantResult)
   186  	}
   187  	if err != nil {
   188  		t.Errorf("wrong err: got %s, want nil", err)
   189  	}
   190  }
   191  
   192  // Archive checksum authentication can fail for various reasons. These test
   193  // cases are almost exhaustive, missing only an io.Copy error which is
   194  // difficult to induce.
   195  func TestArchiveChecksumAuthentication_failure(t *testing.T) {
   196  	tests := map[string]struct {
   197  		location PackageLocation
   198  		err      string
   199  	}{
   200  		"missing file": {
   201  			PackageLocalArchive("testdata/no-package-here.zip"),
   202  			"failed to compute checksum for testdata/no-package-here.zip: lstat testdata/no-package-here.zip: no such file or directory",
   203  		},
   204  		"checksum mismatch": {
   205  			PackageLocalArchive("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_linux_amd64.zip"),
   206  			"archive has incorrect checksum zh:4fb39849f2e138eb16a18ba0c682635d781cb8c3b25901dd5a792ade9711f501 (expected zh:0000000000000000000000000000000000000000000000000000000000000000)",
   207  		},
   208  		"invalid location": {
   209  			PackageLocalDir("testdata/filesystem-mirror/tfe.example.com/AwesomeCorp/happycloud/0.1.0-alpha.2/darwin_amd64"),
   210  			"cannot check archive hash for non-archive location testdata/filesystem-mirror/tfe.example.com/AwesomeCorp/happycloud/0.1.0-alpha.2/darwin_amd64",
   211  		},
   212  	}
   213  
   214  	for name, test := range tests {
   215  		t.Run(name, func(t *testing.T) {
   216  			// Zero expected checksum, either because we'll error before we
   217  			// reach it, or we want to force a checksum mismatch
   218  			auth := NewArchiveChecksumAuthentication(Platform{"linux", "amd64"}, [sha256.Size]byte{0})
   219  			result, err := auth.AuthenticatePackage(test.location)
   220  
   221  			if result != nil {
   222  				t.Errorf("wrong result: got %#v, want nil", result)
   223  			}
   224  			if gotErr := err.Error(); gotErr != test.err {
   225  				t.Errorf("wrong err: got %q, want %q", gotErr, test.err)
   226  			}
   227  		})
   228  	}
   229  }
   230  
   231  // Matching checksum authentication takes a SHA256SUMS document, an archive
   232  // filename, and an expected SHA256 hash. On success both return values should
   233  // be nil.
   234  func TestMatchingChecksumAuthentication_success(t *testing.T) {
   235  	// Location is unused
   236  	location := PackageLocalArchive("testdata/my-package.zip")
   237  
   238  	// Two different checksums for other files
   239  	wantSHA256Sum := [sha256.Size]byte{0xde, 0xca, 0xde}
   240  	otherSHA256Sum := [sha256.Size]byte{0xc0, 0xff, 0xee}
   241  
   242  	document := []byte(
   243  		fmt.Sprintf(
   244  			"%x README.txt\n%x my-package.zip\n",
   245  			otherSHA256Sum,
   246  			wantSHA256Sum,
   247  		),
   248  	)
   249  	filename := "my-package.zip"
   250  
   251  	auth := NewMatchingChecksumAuthentication(document, filename, wantSHA256Sum)
   252  	result, err := auth.AuthenticatePackage(location)
   253  
   254  	if result != nil {
   255  		t.Errorf("wrong result: got %#v, want nil", result)
   256  	}
   257  	if err != nil {
   258  		t.Errorf("wrong err: got %s, want nil", err)
   259  	}
   260  }
   261  
   262  // Matching checksum authentication can fail for three reasons: no checksum
   263  // in the document for the filename, invalid checksum value, and non-matching
   264  // checksum value.
   265  func TestMatchingChecksumAuthentication_failure(t *testing.T) {
   266  	wantSHA256Sum := [sha256.Size]byte{0xde, 0xca, 0xde}
   267  	filename := "my-package.zip"
   268  
   269  	tests := map[string]struct {
   270  		document []byte
   271  		err      string
   272  	}{
   273  		"no checksum for filename": {
   274  			[]byte(
   275  				fmt.Sprintf(
   276  					"%x README.txt",
   277  					[sha256.Size]byte{0xbe, 0xef},
   278  				),
   279  			),
   280  			`checksum list has no SHA-256 hash for "my-package.zip"`,
   281  		},
   282  		"invalid checksum": {
   283  			[]byte(
   284  				fmt.Sprintf(
   285  					"%s README.txt\n%s my-package.zip",
   286  					"horses",
   287  					"chickens",
   288  				),
   289  			),
   290  			`checksum list has invalid SHA256 hash "chickens": encoding/hex: invalid byte: U+0068 'h'`,
   291  		},
   292  		"checksum mismatch": {
   293  			[]byte(
   294  				fmt.Sprintf(
   295  					"%x README.txt\n%x my-package.zip",
   296  					[sha256.Size]byte{0xbe, 0xef},
   297  					[sha256.Size]byte{0xc0, 0xff, 0xee},
   298  				),
   299  			),
   300  			"checksum list has unexpected SHA-256 hash c0ffee0000000000000000000000000000000000000000000000000000000000 (expected decade0000000000000000000000000000000000000000000000000000000000)",
   301  		},
   302  	}
   303  
   304  	for name, test := range tests {
   305  		t.Run(name, func(t *testing.T) {
   306  			// Location is unused
   307  			location := PackageLocalArchive("testdata/my-package.zip")
   308  
   309  			auth := NewMatchingChecksumAuthentication(test.document, filename, wantSHA256Sum)
   310  			result, err := auth.AuthenticatePackage(location)
   311  
   312  			if result != nil {
   313  				t.Errorf("wrong result: got %#v, want nil", result)
   314  			}
   315  			if gotErr := err.Error(); gotErr != test.err {
   316  				t.Errorf("wrong err: got %q, want %q", gotErr, test.err)
   317  			}
   318  		})
   319  	}
   320  }
   321  
   322  // Signature authentication takes a checksum document, a signature, and a list
   323  // of signing keys. If the document is signed by one of the given keys, the
   324  // authentication is successful. The value of the result depends on the signing
   325  // key and its trust signature.
   326  func TestSignatureAuthentication_success(t *testing.T) {
   327  	tests := map[string]struct {
   328  		signature string
   329  		keys      []SigningKey
   330  		result    PackageAuthenticationResult
   331  	}{
   332  		"partner provider": {
   333  			testAuthorSignatureGoodBase64,
   334  			[]SigningKey{
   335  				{
   336  					ASCIIArmor:     testAuthorKeyArmor,
   337  					TrustSignature: testAuthorKeyTrustSignatureArmor,
   338  				},
   339  			},
   340  			PackageAuthenticationResult{
   341  				result: partnerProvider,
   342  				KeyID:  testAuthorKeyID,
   343  			},
   344  		},
   345  		"community provider": {
   346  			testAuthorSignatureGoodBase64,
   347  			[]SigningKey{
   348  				{
   349  					ASCIIArmor: testAuthorKeyArmor,
   350  				},
   351  			},
   352  			PackageAuthenticationResult{
   353  				result: communityProvider,
   354  				KeyID:  testAuthorKeyID,
   355  			},
   356  		},
   357  		"multiple signing keys": {
   358  			testAuthorSignatureGoodBase64,
   359  			[]SigningKey{
   360  				{
   361  					ASCIIArmor: HashicorpPartnersKey,
   362  				},
   363  				{
   364  					ASCIIArmor: testAuthorKeyArmor,
   365  				},
   366  			},
   367  			PackageAuthenticationResult{
   368  				result: communityProvider,
   369  				KeyID:  testAuthorKeyID,
   370  			},
   371  		},
   372  	}
   373  
   374  	for name, test := range tests {
   375  		t.Run(name, func(t *testing.T) {
   376  			// Location is unused
   377  			location := PackageLocalArchive("testdata/my-package.zip")
   378  
   379  			signature, err := base64.StdEncoding.DecodeString(test.signature)
   380  			if err != nil {
   381  				t.Fatal(err)
   382  			}
   383  
   384  			auth := NewSignatureAuthentication([]byte(testShaSumsPlaceholder), signature, test.keys)
   385  			result, err := auth.AuthenticatePackage(location)
   386  
   387  			if result == nil || *result != test.result {
   388  				t.Errorf("wrong result: got %#v, want %#v", result, test.result)
   389  			}
   390  			if err != nil {
   391  				t.Errorf("wrong err: got %s, want nil", err)
   392  			}
   393  		})
   394  	}
   395  }
   396  
   397  func TestNewSignatureAuthentication_success(t *testing.T) {
   398  	tests := map[string]struct {
   399  		signature string
   400  		keys      []SigningKey
   401  		result    PackageAuthenticationResult
   402  	}{
   403  		"official provider": {
   404  			testHashicorpSignatureGoodBase64,
   405  			[]SigningKey{
   406  				{
   407  					ASCIIArmor: HashicorpPublicKey,
   408  				},
   409  			},
   410  			PackageAuthenticationResult{
   411  				result: officialProvider,
   412  				KeyID:  testHashiCorpPublicKeyID,
   413  			},
   414  		},
   415  	}
   416  
   417  	for name, test := range tests {
   418  		t.Run(name, func(t *testing.T) {
   419  			// Location is unused
   420  			location := PackageLocalArchive("testdata/my-package.zip")
   421  
   422  			signature, err := base64.StdEncoding.DecodeString(test.signature)
   423  			if err != nil {
   424  				t.Fatal(err)
   425  			}
   426  
   427  			auth := NewSignatureAuthentication([]byte(testProviderShaSums), signature, test.keys)
   428  			result, err := auth.AuthenticatePackage(location)
   429  
   430  			if result == nil || *result != test.result {
   431  				t.Errorf("wrong result: got %#v, want %#v", result, test.result)
   432  			}
   433  			if err != nil {
   434  				t.Errorf("wrong err: got %s, want nil", err)
   435  			}
   436  		})
   437  	}
   438  }
   439  
   440  // Signature authentication can fail for many reasons, most of which are due
   441  // to OpenPGP failures from malformed keys or signatures.
   442  func TestSignatureAuthentication_failure(t *testing.T) {
   443  	tests := map[string]struct {
   444  		signature string
   445  		keys      []SigningKey
   446  		err       string
   447  	}{
   448  		"invalid key": {
   449  			testHashicorpSignatureGoodBase64,
   450  			[]SigningKey{
   451  				{
   452  					ASCIIArmor: "invalid PGP armor value",
   453  				},
   454  			},
   455  			"error decoding signing key: openpgp: invalid argument: no armored data found",
   456  		},
   457  		"invalid signature": {
   458  			testSignatureBadBase64,
   459  			[]SigningKey{
   460  				{
   461  					ASCIIArmor: testAuthorKeyArmor,
   462  				},
   463  			},
   464  			"error checking signature: openpgp: invalid data: signature subpacket truncated",
   465  		},
   466  		"no keys match signature": {
   467  			testAuthorSignatureGoodBase64,
   468  			[]SigningKey{
   469  				{
   470  					ASCIIArmor: HashicorpPublicKey,
   471  				},
   472  			},
   473  			"authentication signature from unknown issuer",
   474  		},
   475  		"invalid trust signature": {
   476  			testAuthorSignatureGoodBase64,
   477  			[]SigningKey{
   478  				{
   479  					ASCIIArmor:     testAuthorKeyArmor,
   480  					TrustSignature: "invalid PGP armor value",
   481  				},
   482  			},
   483  			"error decoding trust signature: EOF",
   484  		},
   485  		"unverified trust signature": {
   486  			testAuthorSignatureGoodBase64,
   487  			[]SigningKey{
   488  				{
   489  					ASCIIArmor:     testAuthorKeyArmor,
   490  					TrustSignature: testOtherKeyTrustSignatureArmor,
   491  				},
   492  			},
   493  			"error verifying trust signature: openpgp: invalid signature: hash tag doesn't match",
   494  		},
   495  	}
   496  
   497  	for name, test := range tests {
   498  		t.Run(name, func(t *testing.T) {
   499  			// Location is unused
   500  			location := PackageLocalArchive("testdata/my-package.zip")
   501  
   502  			signature, err := base64.StdEncoding.DecodeString(test.signature)
   503  			if err != nil {
   504  				t.Fatal(err)
   505  			}
   506  
   507  			auth := NewSignatureAuthentication([]byte(testShaSumsPlaceholder), signature, test.keys)
   508  			result, err := auth.AuthenticatePackage(location)
   509  
   510  			if result != nil {
   511  				t.Errorf("wrong result: got %#v, want nil", result)
   512  			}
   513  			if gotErr := err.Error(); gotErr != test.err {
   514  				t.Errorf("wrong err: got %s, want %s", gotErr, test.err)
   515  			}
   516  		})
   517  	}
   518  }
   519  
   520  func TestSignatureAuthentication_acceptableHashes(t *testing.T) {
   521  	auth := NewSignatureAuthentication([]byte(testShaSumsRealistic), nil, nil)
   522  	authWithHashes, ok := auth.(PackageAuthenticationHashes)
   523  	if !ok {
   524  		t.Fatalf("%T does not implement PackageAuthenticationHashes", auth)
   525  	}
   526  	got := authWithHashes.AcceptableHashes()
   527  	want := []Hash{
   528  		// These are the hashes encoded in constant testShaSumsRealistic
   529  		"zh:7d7e888fdd28abfe00894f9055209b9eec785153641de98e6852aa071008d4ee",
   530  		"zh:f8b6cf9ade087c17826d49d89cef21261cdc22bd27065bbc5b27d7dbf7fbbf6c",
   531  		"zh:a5ba9945606bb7bfb821ba303957eeb40dd9ee4e706ba8da1eaf7cbeb0356e63",
   532  		"zh:df3a5a8d6ffff7bacf19c92d10d0d500f98169ea17b3764b01a789f563d1aad7",
   533  		"zh:086119a26576d06b8281a97e8644380da89ce16197cd955f74ea5ee664e9358b",
   534  		"zh:1e5f7a5f3ade7b8b1d1d59c5cea2e1a2f8d2f8c3f41962dbbe8647e222be8239",
   535  		"zh:0e9fd0f3e2254b526a0e81e0cfdfc82583b0cd343778c53ead21aa7d52f776d7",
   536  		"zh:66a947e7de1c74caf9f584c3ed4e91d2cb1af6fe5ce8abaf1cf8f7ff626a09d1",
   537  		"zh:def1b73849bec0dc57a04405847921bf9206c75b52ae9de195476facb26bd85e",
   538  		"zh:48f1826ec31d6f104e46cc2022b41f30cd1019ef48eaec9697654ef9ec37a879",
   539  		"zh:17e0b496022bc4e4137be15e96d2b051c8acd6e14cb48d9b13b262330464f6cc",
   540  		"zh:2696c86228f491bc5425561c45904c9ce39b1c676b1e17734cb2ee6b578c4bcd",
   541  	}
   542  	if diff := cmp.Diff(want, got); diff != "" {
   543  		t.Errorf("wrong result\n%s", diff)
   544  	}
   545  }
   546  
   547  const testAuthorKeyID = `37A6AB3BCF2C170A`
   548  
   549  // testAuthorKeyArmor is test key ID 5BFEEC4317E746008621970637A6AB3BCF2C170A.
   550  const testAuthorKeyArmor = `-----BEGIN PGP PUBLIC KEY BLOCK-----
   551  
   552  mQENBF5vhgYBCAC40OcC2hEx3yGiLhHMbt7DAVEQ0nZwAWy6oL98niknLumBa1VO
   553  nMYshP+o/FKOFatBl8aXhmDo606P6pD9d4Pg/WNehqT7hGNHcAFlm+8qjQAvE5uX
   554  Z/na/Np7dmWasCiL5hYyHEnKU/XFpc9KyicbkS7n8igP1LEb8xDD1pMLULQsQHA4
   555  258asvtwjoYTZIij1I6bUE178bGFPNCfj+FzQM8nKzPpDVxZ7njN9c2sB9FEdJ1+
   556  S9mZQNK5PbJuEAOpD5Jp9BnGE16jsLUhDmvGHBjFZAXMBkNSloEMHhs2ty9lEzoF
   557  eJmJx7XCGw+ds1SWp4MsHQPWzXxAlrfa4GMlABEBAAG0R1RlcnJhZm9ybSBUZXN0
   558  aW5nIChwbHVnaW4vZGlzY292ZXJ5LykgPHRlcnJhZm9ybSt0ZXN0aW5nQGhhc2hp
   559  Y29ycC5jb20+iQFOBBMBCAA4FiEEW/7sQxfnRgCGIZcGN6arO88sFwoFAl5vhgYC
   560  GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQN6arO88sFwpWvQf/apaMu4Bm
   561  ea8AGjdl9acQhHBpWsyiHLIfZvN11xxN/f3+YITvPXIe2PMgveqNfXxu6PIeZGDb
   562  0DBvnBQy/vqmA+sCQ8t8+kIWdfZ1EeM2YcXdmAEtriooLvc85JFYjafLIKSj9N7o
   563  V/R/e1BCW/v1/7Je47c+6FSt3HHhwyT5AZ3BCq1zpw6PeCDSQ/gZr3Mvq4CjeLA/
   564  K+8TM3KyOF4qBGDvzGzp/t9umQSS2L0ozd90lxJtf5Q8ozqDaBiDo+f/osXT2EvN
   565  VwPP/xh/gABkXiNrPylFbeD+XPAC4N7NmYK5aPDzRYXXknP8e9PDMykoJKZ+bSdz
   566  F3IZ4q5RDHmmNbkBDQReb4YGAQgAt15e1F8TPQQm1jK8+scypHgfmPHbp7Qsulo1
   567  GTcUd8QmhbR4kayuLDEpJYzq6+IoTM4TPqsdVuq/1Nwey9oyK0wXk/SUR29nRIQh
   568  3GBg7JVg1YsObsfVTvEflYOdjk8T/Udqs4I6HnmSbtzsaohzybutpWXPUkW8OzFI
   569  ATwfVTrrz70Yxs+ly0nSEH2Yf+kg2uYZvv5KsJ3MNENhXnHnlaTy2IfhsxAX0xOG
   570  pa9fXV3NzdEbl0mYaEzMi77qRAyIQ9VrIL5F0yY/LlbpLSl6xk2+BB2v3a1Ey6SJ
   571  w4/le6AM0wlH2hKPCTlkvM0IvUWjlzrPzCkeu027iVc+fqdyiQARAQABiQE2BBgB
   572  CAAgFiEEW/7sQxfnRgCGIZcGN6arO88sFwoFAl5vhgYCGwwACgkQN6arO88sFwqz
   573  nAf/eF4oZG9F8sJX01mVdDm/L7Uthe4xjTdl7jwV4ygNX+pCyWrww3qc3qbd3QKg
   574  CFqIt/TAPE/OxHxCFuxalQefpOqfxjKzvcktxzWmpgxaWsvHaXiS4bKBPz78N/Ke
   575  MUtcjGHyLeSzYPUfjquqDzQxqXidRYhyHGSy9c0NKZ6wCElLZ6KcmCQb4sZxVwfu
   576  ssjwAFbPMp1nr0f5SWCJfhTh7QF7lO2ldJaKMlcBM8aebmqFQ52P7ZWOFcgeerng
   577  G7Zdrci1KEd943HhzDCsUFz4gJwbvUyiAYb2ddndpUBkYwCB/XrHWPOSnGxHgZoo
   578  1gIqed9OV/+s5wKxZPjL0pCStQ==
   579  =mYqJ
   580  -----END PGP PUBLIC KEY BLOCK-----`
   581  
   582  // testAuthorKeyTrustSignatureArmor is a trust signature of the data in
   583  // testAuthorKeyArmor signed with HashicorpPartnersKey.
   584  const testAuthorKeyTrustSignatureArmor = `-----BEGIN PGP SIGNATURE-----
   585  
   586  iQIzBAABCAAdFiEEUYkGV8Ws20uCMIZWfXLUJo5GYPwFAl5w9+YACgkQfXLUJo5G
   587  YPwjRBAAvy9jo3vvetb4qx/z2qhbRH2JbZN9byKuqlIggPzDhhaIsVJVZ9L6H6bE
   588  AMgPe/NaH58wfiqMYenulYxj9tZwJORT/OK0Y9ZFXXZk6kWPMNv7TEppyB0wKgqq
   589  ORKf07KjDcVQslDG9ARgnvDq2GA4UTHxhT0chKHdIKeDLmTm0VSkfNeOhQIkW7vB
   590  S/WT9y78319QJek8OKwJo0Jv0O93rvZZI0JFjXGtP15XNBfObMtPXn3l8qoLzhsv
   591  pJJG/u+BsVZ+y1JDQQlHaD1P2TLW/nGymFq12k693IOCmNyaIOa01Wa9B/j3a3RY
   592  v4SdkULvJKbttNMNBgIMJ74wZp5EUhEFs68sllrIrmthH8bW2fbcHEQ1g/MJCe3+
   593  43c9aoW8yNQmuEe7yre9lgqcJOIOxlb5XEJhH0Lh+8OBi5aHA/5wXGU5WrhWqHCR
   594  npXBsNqy2sKUuVkEzvn3Hd6aoKncVLrgNR8xA3VP86jJhawvO+M+YYMr1wOVHc/I
   595  PYq9hlyUR8qJ/0RpnaIE1iLbPYfEpGTg7oHORpbQVoZAUwMN/Sdox7sMkqCOb1RJ
   596  Cmy9J5o7iiNOoshvps5cxcbsM7LNfbf0vDhWpckAvsQehrS1mfVuFHkIiotVQhH1
   597  QXPfvB2cVF/SxMqqHWpnT+8c8klfS03kXSb0BdknrQ4DNPq1H5A=
   598  =3A1s
   599  -----END PGP SIGNATURE-----`
   600  
   601  // testOtherKeyTrustSignatureArmor is a trust signature of another key (not the
   602  // author key), signed with HashicorpPartnersKey.
   603  const testOtherKeyTrustSignatureArmor = `-----BEGIN PGP SIGNATURE-----
   604  
   605  iQIzBAABCAAdFiEEUYkGV8Ws20uCMIZWfXLUJo5GYPwFAl6POvsACgkQfXLUJo5G
   606  YPyGihAAomM1kGmrC5KRgWQ+V47r8wFoIkhsTgAYb9ENOzn/RVJt3SJSstcKxfA3
   607  7HW5R4kqAoXH1hcPYpUcOcdeAvtZxjGRQ9JgErV8NBg6sR11aQccCzAG4Hy0hWav
   608  /jB5NzTEX5JFEXH6WhpWI1avh0l2j6JxO1K1s+5+5PI3KbuO+XSqeZ3QmUz9FwGu
   609  pr0J6oYcERupzrpnmgMb5fbkpHfzffR2/MOYdF9Hae4EvDS1b7tokuuKsStNnCm0
   610  ge7PFdekwbj/OiQrQlqM1pOw2siPX3ouWCtW8oExm9tAxNw31Bn2g3oaNMkHMqJd
   611  hlVUZlqeJMyylUat3cY7GTQONfCnoyUHe/wv8exBUbV3v2glp9y2g9i2XmXkHOrV
   612  Z+pnNBc+jdp3a4O0Y8fXXZdjiIolZKY8BbvzheuMrQQIOmw4N3KrZbTpLKuqz8rb
   613  h8bqUbU42oWcJmBvzF4NZ4tQ+aFHs4CbOnjfDfS14baQr2Gqo9BqTfrzS5Pbs8lq
   614  AhY0r+zi71lQ1rBfgZfjd8zWlOzpDO//nwKhGCqYOWke/C/T6o0zxM0R4uR4zXwT
   615  KhvXK8/kK/L8Flaxqme0d5bzXLbsMe9I6I76DY5iNhkiFnnWt4+FhGoIDR03MTKS
   616  SnHodBLlpKLyUXi36DCDy/iKVsieqLsAdcYe0nQFuhoQcOme33A=
   617  =aHOG
   618  -----END PGP SIGNATURE-----`
   619  
   620  // testShaSumsPlaceholder is a string that represents a signed document that
   621  // the signature authenticator will check. Some of the signature valuesin
   622  // other constants in this file are signing this string.
   623  const testShaSumsPlaceholder = "example shasums data"
   624  
   625  // testShaSumsRealistic is a more realistic SHA256SUMS document that we can use
   626  // to test the AcceptableHashes method. The signature values in other constants
   627  // in this file do not sign this string.
   628  const testShaSumsRealistic = `7d7e888fdd28abfe00894f9055209b9eec785153641de98e6852aa071008d4ee  terraform_0.14.0-alpha20200923_darwin_amd64.zip
   629  f8b6cf9ade087c17826d49d89cef21261cdc22bd27065bbc5b27d7dbf7fbbf6c  terraform_0.14.0-alpha20200923_freebsd_386.zip
   630  a5ba9945606bb7bfb821ba303957eeb40dd9ee4e706ba8da1eaf7cbeb0356e63  terraform_0.14.0-alpha20200923_freebsd_amd64.zip
   631  df3a5a8d6ffff7bacf19c92d10d0d500f98169ea17b3764b01a789f563d1aad7  terraform_0.14.0-alpha20200923_freebsd_arm.zip
   632  086119a26576d06b8281a97e8644380da89ce16197cd955f74ea5ee664e9358b  terraform_0.14.0-alpha20200923_linux_386.zip
   633  1e5f7a5f3ade7b8b1d1d59c5cea2e1a2f8d2f8c3f41962dbbe8647e222be8239  terraform_0.14.0-alpha20200923_linux_amd64.zip
   634  0e9fd0f3e2254b526a0e81e0cfdfc82583b0cd343778c53ead21aa7d52f776d7  terraform_0.14.0-alpha20200923_linux_arm.zip
   635  66a947e7de1c74caf9f584c3ed4e91d2cb1af6fe5ce8abaf1cf8f7ff626a09d1  terraform_0.14.0-alpha20200923_openbsd_386.zip
   636  def1b73849bec0dc57a04405847921bf9206c75b52ae9de195476facb26bd85e  terraform_0.14.0-alpha20200923_openbsd_amd64.zip
   637  48f1826ec31d6f104e46cc2022b41f30cd1019ef48eaec9697654ef9ec37a879  terraform_0.14.0-alpha20200923_solaris_amd64.zip
   638  17e0b496022bc4e4137be15e96d2b051c8acd6e14cb48d9b13b262330464f6cc  terraform_0.14.0-alpha20200923_windows_386.zip
   639  2696c86228f491bc5425561c45904c9ce39b1c676b1e17734cb2ee6b578c4bcd  terraform_0.14.0-alpha20200923_windows_amd64.zip`
   640  
   641  // testAuthorSignatureGoodBase64 is a signature of testShaSums signed with
   642  // testAuthorKeyArmor, which represents the SHA256SUMS.sig file downloaded for
   643  // a release.
   644  const testAuthorSignatureGoodBase64 = `iQEzBAABCAAdFiEEW/7sQxfnRgCGIZcGN6arO88s` +
   645  	`FwoFAl5vh7gACgkQN6arO88sFwrAlQf6Al77qzjxNIj+NQNJfBGYUE5jHIgcuWOs1IPRTYUI` +
   646  	`rHQIUU2RVrdHoAefKTKNzGde653JK/pYTflSV+6ini3/aZZnXlF6t001w3wswmakdwTr0hXx` +
   647  	`Ez/hHYio72Gpn7+T/L+nl6dKkjeGqd/Kor5x2TY9uYB737ESmAe5T8ZlPaGMFHh0mYlNTeRq` +
   648  	`4qIKqL6DwddBF4Ju2svn2MeNMGfE358H31mxAl2k4PPrwBTR1sFUCUOzAXVA/g9Ov5Y9ni2G` +
   649  	`rkTahBtV9yuUUd1D+oRTTTdP0bj3A+3xxXmKTBhRuvurydPTicKuWzeILIJkcwp7Kl5UbI2N` +
   650  	`n1ayZdaCIw/r4w==`
   651  
   652  // testSignatureBadBase64 is an invalid signature.
   653  const testSignatureBadBase64 = `iQEzBAABCAAdFiEEW/7sQxfnRgCGIZcGN6arO88s` +
   654  	`4qIKqL6DwddBF4Ju2svn2MeNMGfE358H31mxAl2k4PPrwBTR1sFUCUOzAXVA/g9Ov5Y9ni2G` +
   655  	`rkTahBtV9yuUUd1D+oRTTTdP0bj3A+3xxXmKTBhRuvurydPTicKuWzeILIJkcwp7Kl5UbI2N` +
   656  	`n1ayZdaCIw/r4w==`
   657  
   658  // testHashiCorpPublicKeyID is the Key ID of the HashiCorpPublicKey.
   659  const testHashiCorpPublicKeyID = `34365D9472D7468F`
   660  
   661  const testProviderShaSums = `fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e  terraform-provider-null_3.1.0_darwin_amd64.zip
   662  9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2  terraform-provider-null_3.1.0_darwin_arm64.zip
   663  a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e  terraform-provider-null_3.1.0_freebsd_386.zip
   664  5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521  terraform-provider-null_3.1.0_freebsd_amd64.zip
   665  fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b  terraform-provider-null_3.1.0_freebsd_arm.zip
   666  c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d  terraform-provider-null_3.1.0_linux_386.zip
   667  53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515  terraform-provider-null_3.1.0_linux_amd64.zip
   668  cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8  terraform-provider-null_3.1.0_linux_arm64.zip
   669  e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70  terraform-provider-null_3.1.0_linux_arm.zip
   670  a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53  terraform-provider-null_3.1.0_windows_386.zip
   671  02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2  terraform-provider-null_3.1.0_windows_amd64.zip
   672  `
   673  
   674  // testHashicorpSignatureGoodBase64 is a signature of testProviderShaSums signed with
   675  // HashicorpPublicKey, which represents the SHA256SUMS.sig file downloaded for
   676  // an official release.
   677  const testHashicorpSignatureGoodBase64 = `wsFcBAABCAAQBQJgga+GCRCwtEEJdoW2dgAA` +
   678  	`o0YQAAW911BGDr2WHLo5NwcZenwHyxL5DX9g+4BknKbc/WxRC1hD8Afi3eygZk1yR6eT4Gp2H` +
   679  	`yNOwCjGL1PTONBumMfj9udIeuX8onrJMMvjFHh+bORGxBi4FKr4V3b2ZV1IYOjWMEyyTGRDvw` +
   680  	`SCdxBkp3apH3s2xZLmRoAj84JZ4KaxGF7hlT0j4IkNyQKd2T5cCByN9DV80+x+HtzaOieFwJL` +
   681  	`97iyGj6aznXfKfslK6S4oIrVTwyLTrQbxSxA0LsdUjRPHnJamL3sFOG77qUEUoXG3r61yi5vW` +
   682  	`V4P5gCH/+C+VkfGHqaB1s0jHYLxoTEXtwthe66MydDBPe2Hd0J12u9ppOIeK3leeb4uiixWIi` +
   683  	`rNdpWyjr/LU1KKWPxsDqMGYJ9TexyWkXjEpYmIEiY1Rxar8jrLh+FqVAhxRJajjgSRu5pZj50` +
   684  	`CNeKmmbyolLhPCmICjYYU/xKPGXSyDFqonVVyMWCSpO+8F38OmwDQHIk5AWyc8hPOAZ+g5N95` +
   685  	`cfUAzEqlvmNvVHQIU40Y6/Ip2HZzzFCLKQkMP1aDakYHq5w4ZO/ucjhKuoh1HDQMuMnZSu4eo` +
   686  	`2nMTBzYZnUxwtROrJZF1t103avbmP2QE/GaPvLIQn7o5WMV3ZcPCJ+szzzby7H2e33WIynrY/` +
   687  	`95ensBxh7mGFbcQ1C59b5o7viwIaaY2`
   688  
   689  // entityString function is used for logging the signing key.
   690  func TestEntityString(t *testing.T) {
   691  	var tests = []struct {
   692  		name     string
   693  		entity   *openpgp.Entity
   694  		expected string
   695  	}{
   696  		{
   697  			"nil",
   698  			nil,
   699  			"",
   700  		},
   701  		{
   702  			"testAuthorKeyArmor",
   703  			testReadArmoredEntity(t, testAuthorKeyArmor),
   704  			"37A6AB3BCF2C170A Terraform Testing (plugin/discovery/) <terraform+testing@hashicorp.com>",
   705  		},
   706  		{
   707  			"HashicorpPublicKey",
   708  			testReadArmoredEntity(t, HashicorpPublicKey),
   709  			"34365D9472D7468F HashiCorp Security (hashicorp.com/security) <security@hashicorp.com>",
   710  		},
   711  		{
   712  			"HashicorpPartnersKey",
   713  			testReadArmoredEntity(t, HashicorpPartnersKey),
   714  			"7D72D4268E4660FC HashiCorp Security (Terraform Partner Signing) <security+terraform@hashicorp.com>",
   715  		},
   716  	}
   717  
   718  	for _, tt := range tests {
   719  		t.Run(tt.name, func(t *testing.T) {
   720  			actual := entityString(tt.entity)
   721  			if actual != tt.expected {
   722  				t.Errorf("expected %s, actual %s", tt.expected, actual)
   723  			}
   724  		})
   725  	}
   726  }
   727  
   728  func testReadArmoredEntity(t *testing.T, armor string) *openpgp.Entity {
   729  	data := strings.NewReader(armor)
   730  
   731  	el, err := openpgp.ReadArmoredKeyRing(data)
   732  	if err != nil {
   733  		t.Fatal(err)
   734  	}
   735  
   736  	if count := len(el); count != 1 {
   737  		t.Fatalf("expected 1 entity, got %d", count)
   738  	}
   739  
   740  	return el[0]
   741  }