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