742 lines
27 KiB
Go
742 lines
27 KiB
Go
package getproviders
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
|
// TODO: replace crypto/openpgp since it is deprecated
|
|
// https://github.com/golang/go/issues/44226
|
|
//lint:file-ignore SA1019 openpgp is deprecated but there are no good alternatives yet
|
|
"golang.org/x/crypto/openpgp"
|
|
)
|
|
|
|
func TestPackageAuthenticationResult(t *testing.T) {
|
|
tests := []struct {
|
|
result *PackageAuthenticationResult
|
|
want string
|
|
}{
|
|
{
|
|
nil,
|
|
"unauthenticated",
|
|
},
|
|
{
|
|
&PackageAuthenticationResult{result: verifiedChecksum},
|
|
"verified checksum",
|
|
},
|
|
{
|
|
&PackageAuthenticationResult{result: officialProvider},
|
|
"signed by HashiCorp",
|
|
},
|
|
{
|
|
&PackageAuthenticationResult{result: partnerProvider},
|
|
"signed by a HashiCorp partner",
|
|
},
|
|
{
|
|
&PackageAuthenticationResult{result: communityProvider},
|
|
"self-signed",
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
if got := test.result.String(); got != test.want {
|
|
t.Errorf("wrong value: got %q, want %q", got, test.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// mockAuthentication is an implementation of the PackageAuthentication
|
|
// interface which returns fixed values. This is used to test the combining
|
|
// logic of PackageAuthenticationAll.
|
|
type mockAuthentication struct {
|
|
result packageAuthenticationResult
|
|
err error
|
|
}
|
|
|
|
func (m mockAuthentication) AuthenticatePackage(localLocation PackageLocation) (*PackageAuthenticationResult, error) {
|
|
if m.err == nil {
|
|
return &PackageAuthenticationResult{result: m.result}, nil
|
|
} else {
|
|
return nil, m.err
|
|
}
|
|
}
|
|
|
|
var _ PackageAuthentication = (*mockAuthentication)(nil)
|
|
|
|
// If all authentications succeed, the returned result should come from the
|
|
// last authentication.
|
|
func TestPackageAuthenticationAll_success(t *testing.T) {
|
|
result, err := PackageAuthenticationAll(
|
|
&mockAuthentication{result: verifiedChecksum},
|
|
&mockAuthentication{result: communityProvider},
|
|
).AuthenticatePackage(nil)
|
|
|
|
want := PackageAuthenticationResult{result: communityProvider}
|
|
if result == nil || *result != want {
|
|
t.Errorf("wrong result: want %#v, got %#v", want, result)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("wrong err: got %#v, want nil", err)
|
|
}
|
|
}
|
|
|
|
// If an authentication fails, its error should be returned along with a nil
|
|
// result.
|
|
func TestPackageAuthenticationAll_failure(t *testing.T) {
|
|
someError := errors.New("some error")
|
|
result, err := PackageAuthenticationAll(
|
|
&mockAuthentication{result: verifiedChecksum},
|
|
&mockAuthentication{err: someError},
|
|
&mockAuthentication{result: communityProvider},
|
|
).AuthenticatePackage(nil)
|
|
|
|
if result != nil {
|
|
t.Errorf("wrong result: got %#v, want nil", result)
|
|
}
|
|
if err != someError {
|
|
t.Errorf("wrong err: got %#v, want %#v", err, someError)
|
|
}
|
|
}
|
|
|
|
// Package hash authentication requires a zip file or directory fixture and a
|
|
// known-good set of hashes, of which the authenticator will pick one. The
|
|
// result should be "verified checksum".
|
|
func TestPackageHashAuthentication_success(t *testing.T) {
|
|
// Location must be a PackageLocalArchive path
|
|
location := PackageLocalDir("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/2.0.0/linux_amd64")
|
|
|
|
wantHashes := []Hash{
|
|
// Known-good HashV1 result for this directory
|
|
Hash("h1:qjsREM4DqEWECD43FcPqddZ9oxCG+IaMTxvWPciS05g="),
|
|
}
|
|
|
|
auth := NewPackageHashAuthentication(Platform{"linux", "amd64"}, wantHashes)
|
|
result, err := auth.AuthenticatePackage(location)
|
|
|
|
wantResult := PackageAuthenticationResult{result: verifiedChecksum}
|
|
if result == nil || *result != wantResult {
|
|
t.Errorf("wrong result: got %#v, want %#v", result, wantResult)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("wrong err: got %s, want nil", err)
|
|
}
|
|
}
|
|
|
|
// Package has authentication can fail for various reasons.
|
|
func TestPackageHashAuthentication_failure(t *testing.T) {
|
|
tests := map[string]struct {
|
|
location PackageLocation
|
|
err string
|
|
}{
|
|
"missing file": {
|
|
PackageLocalArchive("testdata/no-package-here.zip"),
|
|
"failed to verify provider package checksums: lstat testdata/no-package-here.zip: no such file or directory",
|
|
},
|
|
"checksum mismatch": {
|
|
PackageLocalDir("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/2.0.0/linux_amd64"),
|
|
"provider package doesn't match the expected checksum \"h1:invalid\"",
|
|
},
|
|
"invalid zip file": {
|
|
PackageLocalArchive("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_linux_amd64.zip"),
|
|
"failed to verify provider package checksums: zip: not a valid zip file",
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
// Invalid expected hash, either because we'll error before we
|
|
// reach it, or we want to force a checksum mismatch.
|
|
auth := NewPackageHashAuthentication(Platform{"linux", "amd64"}, []Hash{"h1:invalid"})
|
|
result, err := auth.AuthenticatePackage(test.location)
|
|
|
|
if result != nil {
|
|
t.Errorf("wrong result: got %#v, want nil", result)
|
|
}
|
|
if gotErr := err.Error(); gotErr != test.err {
|
|
t.Errorf("wrong err: got %q, want %q", gotErr, test.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Archive checksum authentication requires a file fixture and a known-good
|
|
// SHA256 hash. The result should be "verified checksum".
|
|
func TestArchiveChecksumAuthentication_success(t *testing.T) {
|
|
// Location must be a PackageLocalArchive path
|
|
location := PackageLocalArchive("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_linux_amd64.zip")
|
|
|
|
// Known-good SHA256 hash for this archive
|
|
wantSHA256Sum := [sha256.Size]byte{
|
|
0x4f, 0xb3, 0x98, 0x49, 0xf2, 0xe1, 0x38, 0xeb,
|
|
0x16, 0xa1, 0x8b, 0xa0, 0xc6, 0x82, 0x63, 0x5d,
|
|
0x78, 0x1c, 0xb8, 0xc3, 0xb2, 0x59, 0x01, 0xdd,
|
|
0x5a, 0x79, 0x2a, 0xde, 0x97, 0x11, 0xf5, 0x01,
|
|
}
|
|
|
|
auth := NewArchiveChecksumAuthentication(Platform{"linux", "amd64"}, wantSHA256Sum)
|
|
result, err := auth.AuthenticatePackage(location)
|
|
|
|
wantResult := PackageAuthenticationResult{result: verifiedChecksum}
|
|
if result == nil || *result != wantResult {
|
|
t.Errorf("wrong result: got %#v, want %#v", result, wantResult)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("wrong err: got %s, want nil", err)
|
|
}
|
|
}
|
|
|
|
// Archive checksum authentication can fail for various reasons. These test
|
|
// cases are almost exhaustive, missing only an io.Copy error which is
|
|
// difficult to induce.
|
|
func TestArchiveChecksumAuthentication_failure(t *testing.T) {
|
|
tests := map[string]struct {
|
|
location PackageLocation
|
|
err string
|
|
}{
|
|
"missing file": {
|
|
PackageLocalArchive("testdata/no-package-here.zip"),
|
|
"failed to compute checksum for testdata/no-package-here.zip: lstat testdata/no-package-here.zip: no such file or directory",
|
|
},
|
|
"checksum mismatch": {
|
|
PackageLocalArchive("testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_linux_amd64.zip"),
|
|
"archive has incorrect checksum zh:4fb39849f2e138eb16a18ba0c682635d781cb8c3b25901dd5a792ade9711f501 (expected zh:0000000000000000000000000000000000000000000000000000000000000000)",
|
|
},
|
|
"invalid location": {
|
|
PackageLocalDir("testdata/filesystem-mirror/tfe.example.com/AwesomeCorp/happycloud/0.1.0-alpha.2/darwin_amd64"),
|
|
"cannot check archive hash for non-archive location testdata/filesystem-mirror/tfe.example.com/AwesomeCorp/happycloud/0.1.0-alpha.2/darwin_amd64",
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
// Zero expected checksum, either because we'll error before we
|
|
// reach it, or we want to force a checksum mismatch
|
|
auth := NewArchiveChecksumAuthentication(Platform{"linux", "amd64"}, [sha256.Size]byte{0})
|
|
result, err := auth.AuthenticatePackage(test.location)
|
|
|
|
if result != nil {
|
|
t.Errorf("wrong result: got %#v, want nil", result)
|
|
}
|
|
if gotErr := err.Error(); gotErr != test.err {
|
|
t.Errorf("wrong err: got %q, want %q", gotErr, test.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Matching checksum authentication takes a SHA256SUMS document, an archive
|
|
// filename, and an expected SHA256 hash. On success both return values should
|
|
// be nil.
|
|
func TestMatchingChecksumAuthentication_success(t *testing.T) {
|
|
// Location is unused
|
|
location := PackageLocalArchive("testdata/my-package.zip")
|
|
|
|
// Two different checksums for other files
|
|
wantSHA256Sum := [sha256.Size]byte{0xde, 0xca, 0xde}
|
|
otherSHA256Sum := [sha256.Size]byte{0xc0, 0xff, 0xee}
|
|
|
|
document := []byte(
|
|
fmt.Sprintf(
|
|
"%x README.txt\n%x my-package.zip\n",
|
|
otherSHA256Sum,
|
|
wantSHA256Sum,
|
|
),
|
|
)
|
|
filename := "my-package.zip"
|
|
|
|
auth := NewMatchingChecksumAuthentication(document, filename, wantSHA256Sum)
|
|
result, err := auth.AuthenticatePackage(location)
|
|
|
|
if result != nil {
|
|
t.Errorf("wrong result: got %#v, want nil", result)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("wrong err: got %s, want nil", err)
|
|
}
|
|
}
|
|
|
|
// Matching checksum authentication can fail for three reasons: no checksum
|
|
// in the document for the filename, invalid checksum value, and non-matching
|
|
// checksum value.
|
|
func TestMatchingChecksumAuthentication_failure(t *testing.T) {
|
|
wantSHA256Sum := [sha256.Size]byte{0xde, 0xca, 0xde}
|
|
filename := "my-package.zip"
|
|
|
|
tests := map[string]struct {
|
|
document []byte
|
|
err string
|
|
}{
|
|
"no checksum for filename": {
|
|
[]byte(
|
|
fmt.Sprintf(
|
|
"%x README.txt",
|
|
[sha256.Size]byte{0xbe, 0xef},
|
|
),
|
|
),
|
|
`checksum list has no SHA-256 hash for "my-package.zip"`,
|
|
},
|
|
"invalid checksum": {
|
|
[]byte(
|
|
fmt.Sprintf(
|
|
"%s README.txt\n%s my-package.zip",
|
|
"horses",
|
|
"chickens",
|
|
),
|
|
),
|
|
`checksum list has invalid SHA256 hash "chickens": encoding/hex: invalid byte: U+0068 'h'`,
|
|
},
|
|
"checksum mismatch": {
|
|
[]byte(
|
|
fmt.Sprintf(
|
|
"%x README.txt\n%x my-package.zip",
|
|
[sha256.Size]byte{0xbe, 0xef},
|
|
[sha256.Size]byte{0xc0, 0xff, 0xee},
|
|
),
|
|
),
|
|
"checksum list has unexpected SHA-256 hash c0ffee0000000000000000000000000000000000000000000000000000000000 (expected decade0000000000000000000000000000000000000000000000000000000000)",
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
// Location is unused
|
|
location := PackageLocalArchive("testdata/my-package.zip")
|
|
|
|
auth := NewMatchingChecksumAuthentication(test.document, filename, wantSHA256Sum)
|
|
result, err := auth.AuthenticatePackage(location)
|
|
|
|
if result != nil {
|
|
t.Errorf("wrong result: got %#v, want nil", result)
|
|
}
|
|
if gotErr := err.Error(); gotErr != test.err {
|
|
t.Errorf("wrong err: got %q, want %q", gotErr, test.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Signature authentication takes a checksum document, a signature, and a list
|
|
// of signing keys. If the document is signed by one of the given keys, the
|
|
// authentication is successful. The value of the result depends on the signing
|
|
// key and its trust signature.
|
|
func TestSignatureAuthentication_success(t *testing.T) {
|
|
tests := map[string]struct {
|
|
signature string
|
|
keys []SigningKey
|
|
result PackageAuthenticationResult
|
|
}{
|
|
"partner provider": {
|
|
testAuthorSignatureGoodBase64,
|
|
[]SigningKey{
|
|
{
|
|
ASCIIArmor: testAuthorKeyArmor,
|
|
TrustSignature: testAuthorKeyTrustSignatureArmor,
|
|
},
|
|
},
|
|
PackageAuthenticationResult{
|
|
result: partnerProvider,
|
|
KeyID: testAuthorKeyID,
|
|
},
|
|
},
|
|
"community provider": {
|
|
testAuthorSignatureGoodBase64,
|
|
[]SigningKey{
|
|
{
|
|
ASCIIArmor: testAuthorKeyArmor,
|
|
},
|
|
},
|
|
PackageAuthenticationResult{
|
|
result: communityProvider,
|
|
KeyID: testAuthorKeyID,
|
|
},
|
|
},
|
|
"multiple signing keys": {
|
|
testAuthorSignatureGoodBase64,
|
|
[]SigningKey{
|
|
{
|
|
ASCIIArmor: HashicorpPartnersKey,
|
|
},
|
|
{
|
|
ASCIIArmor: testAuthorKeyArmor,
|
|
},
|
|
},
|
|
PackageAuthenticationResult{
|
|
result: communityProvider,
|
|
KeyID: testAuthorKeyID,
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
// Location is unused
|
|
location := PackageLocalArchive("testdata/my-package.zip")
|
|
|
|
signature, err := base64.StdEncoding.DecodeString(test.signature)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
auth := NewSignatureAuthentication([]byte(testShaSumsPlaceholder), signature, test.keys)
|
|
result, err := auth.AuthenticatePackage(location)
|
|
|
|
if result == nil || *result != test.result {
|
|
t.Errorf("wrong result: got %#v, want %#v", result, test.result)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("wrong err: got %s, want nil", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewSignatureAuthentication_success(t *testing.T) {
|
|
tests := map[string]struct {
|
|
signature string
|
|
keys []SigningKey
|
|
result PackageAuthenticationResult
|
|
}{
|
|
"official provider": {
|
|
testHashicorpSignatureGoodBase64,
|
|
[]SigningKey{
|
|
{
|
|
ASCIIArmor: HashicorpPublicKey,
|
|
},
|
|
},
|
|
PackageAuthenticationResult{
|
|
result: officialProvider,
|
|
KeyID: testHashiCorpPublicKeyID,
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
// Location is unused
|
|
location := PackageLocalArchive("testdata/my-package.zip")
|
|
|
|
signature, err := base64.StdEncoding.DecodeString(test.signature)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
auth := NewSignatureAuthentication([]byte(testProviderShaSums), signature, test.keys)
|
|
result, err := auth.AuthenticatePackage(location)
|
|
|
|
if result == nil || *result != test.result {
|
|
t.Errorf("wrong result: got %#v, want %#v", result, test.result)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("wrong err: got %s, want nil", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Signature authentication can fail for many reasons, most of which are due
|
|
// to OpenPGP failures from malformed keys or signatures.
|
|
func TestSignatureAuthentication_failure(t *testing.T) {
|
|
tests := map[string]struct {
|
|
signature string
|
|
keys []SigningKey
|
|
err string
|
|
}{
|
|
"invalid key": {
|
|
testHashicorpSignatureGoodBase64,
|
|
[]SigningKey{
|
|
{
|
|
ASCIIArmor: "invalid PGP armor value",
|
|
},
|
|
},
|
|
"error decoding signing key: openpgp: invalid argument: no armored data found",
|
|
},
|
|
"invalid signature": {
|
|
testSignatureBadBase64,
|
|
[]SigningKey{
|
|
{
|
|
ASCIIArmor: testAuthorKeyArmor,
|
|
},
|
|
},
|
|
"error checking signature: openpgp: invalid data: signature subpacket truncated",
|
|
},
|
|
"no keys match signature": {
|
|
testAuthorSignatureGoodBase64,
|
|
[]SigningKey{
|
|
{
|
|
ASCIIArmor: HashicorpPublicKey,
|
|
},
|
|
},
|
|
"authentication signature from unknown issuer",
|
|
},
|
|
"invalid trust signature": {
|
|
testAuthorSignatureGoodBase64,
|
|
[]SigningKey{
|
|
{
|
|
ASCIIArmor: testAuthorKeyArmor,
|
|
TrustSignature: "invalid PGP armor value",
|
|
},
|
|
},
|
|
"error decoding trust signature: EOF",
|
|
},
|
|
"unverified trust signature": {
|
|
testAuthorSignatureGoodBase64,
|
|
[]SigningKey{
|
|
{
|
|
ASCIIArmor: testAuthorKeyArmor,
|
|
TrustSignature: testOtherKeyTrustSignatureArmor,
|
|
},
|
|
},
|
|
"error verifying trust signature: openpgp: invalid signature: hash tag doesn't match",
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
// Location is unused
|
|
location := PackageLocalArchive("testdata/my-package.zip")
|
|
|
|
signature, err := base64.StdEncoding.DecodeString(test.signature)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
auth := NewSignatureAuthentication([]byte(testShaSumsPlaceholder), signature, test.keys)
|
|
result, err := auth.AuthenticatePackage(location)
|
|
|
|
if result != nil {
|
|
t.Errorf("wrong result: got %#v, want nil", result)
|
|
}
|
|
if gotErr := err.Error(); gotErr != test.err {
|
|
t.Errorf("wrong err: got %s, want %s", gotErr, test.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
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.
|
|
const testAuthorKeyArmor = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
|
|
mQENBF5vhgYBCAC40OcC2hEx3yGiLhHMbt7DAVEQ0nZwAWy6oL98niknLumBa1VO
|
|
nMYshP+o/FKOFatBl8aXhmDo606P6pD9d4Pg/WNehqT7hGNHcAFlm+8qjQAvE5uX
|
|
Z/na/Np7dmWasCiL5hYyHEnKU/XFpc9KyicbkS7n8igP1LEb8xDD1pMLULQsQHA4
|
|
258asvtwjoYTZIij1I6bUE178bGFPNCfj+FzQM8nKzPpDVxZ7njN9c2sB9FEdJ1+
|
|
S9mZQNK5PbJuEAOpD5Jp9BnGE16jsLUhDmvGHBjFZAXMBkNSloEMHhs2ty9lEzoF
|
|
eJmJx7XCGw+ds1SWp4MsHQPWzXxAlrfa4GMlABEBAAG0R1RlcnJhZm9ybSBUZXN0
|
|
aW5nIChwbHVnaW4vZGlzY292ZXJ5LykgPHRlcnJhZm9ybSt0ZXN0aW5nQGhhc2hp
|
|
Y29ycC5jb20+iQFOBBMBCAA4FiEEW/7sQxfnRgCGIZcGN6arO88sFwoFAl5vhgYC
|
|
GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQN6arO88sFwpWvQf/apaMu4Bm
|
|
ea8AGjdl9acQhHBpWsyiHLIfZvN11xxN/f3+YITvPXIe2PMgveqNfXxu6PIeZGDb
|
|
0DBvnBQy/vqmA+sCQ8t8+kIWdfZ1EeM2YcXdmAEtriooLvc85JFYjafLIKSj9N7o
|
|
V/R/e1BCW/v1/7Je47c+6FSt3HHhwyT5AZ3BCq1zpw6PeCDSQ/gZr3Mvq4CjeLA/
|
|
K+8TM3KyOF4qBGDvzGzp/t9umQSS2L0ozd90lxJtf5Q8ozqDaBiDo+f/osXT2EvN
|
|
VwPP/xh/gABkXiNrPylFbeD+XPAC4N7NmYK5aPDzRYXXknP8e9PDMykoJKZ+bSdz
|
|
F3IZ4q5RDHmmNbkBDQReb4YGAQgAt15e1F8TPQQm1jK8+scypHgfmPHbp7Qsulo1
|
|
GTcUd8QmhbR4kayuLDEpJYzq6+IoTM4TPqsdVuq/1Nwey9oyK0wXk/SUR29nRIQh
|
|
3GBg7JVg1YsObsfVTvEflYOdjk8T/Udqs4I6HnmSbtzsaohzybutpWXPUkW8OzFI
|
|
ATwfVTrrz70Yxs+ly0nSEH2Yf+kg2uYZvv5KsJ3MNENhXnHnlaTy2IfhsxAX0xOG
|
|
pa9fXV3NzdEbl0mYaEzMi77qRAyIQ9VrIL5F0yY/LlbpLSl6xk2+BB2v3a1Ey6SJ
|
|
w4/le6AM0wlH2hKPCTlkvM0IvUWjlzrPzCkeu027iVc+fqdyiQARAQABiQE2BBgB
|
|
CAAgFiEEW/7sQxfnRgCGIZcGN6arO88sFwoFAl5vhgYCGwwACgkQN6arO88sFwqz
|
|
nAf/eF4oZG9F8sJX01mVdDm/L7Uthe4xjTdl7jwV4ygNX+pCyWrww3qc3qbd3QKg
|
|
CFqIt/TAPE/OxHxCFuxalQefpOqfxjKzvcktxzWmpgxaWsvHaXiS4bKBPz78N/Ke
|
|
MUtcjGHyLeSzYPUfjquqDzQxqXidRYhyHGSy9c0NKZ6wCElLZ6KcmCQb4sZxVwfu
|
|
ssjwAFbPMp1nr0f5SWCJfhTh7QF7lO2ldJaKMlcBM8aebmqFQ52P7ZWOFcgeerng
|
|
G7Zdrci1KEd943HhzDCsUFz4gJwbvUyiAYb2ddndpUBkYwCB/XrHWPOSnGxHgZoo
|
|
1gIqed9OV/+s5wKxZPjL0pCStQ==
|
|
=mYqJ
|
|
-----END PGP PUBLIC KEY BLOCK-----`
|
|
|
|
// testAuthorKeyTrustSignatureArmor is a trust signature of the data in
|
|
// testAuthorKeyArmor signed with HashicorpPartnersKey.
|
|
const testAuthorKeyTrustSignatureArmor = `-----BEGIN PGP SIGNATURE-----
|
|
|
|
iQIzBAABCAAdFiEEUYkGV8Ws20uCMIZWfXLUJo5GYPwFAl5w9+YACgkQfXLUJo5G
|
|
YPwjRBAAvy9jo3vvetb4qx/z2qhbRH2JbZN9byKuqlIggPzDhhaIsVJVZ9L6H6bE
|
|
AMgPe/NaH58wfiqMYenulYxj9tZwJORT/OK0Y9ZFXXZk6kWPMNv7TEppyB0wKgqq
|
|
ORKf07KjDcVQslDG9ARgnvDq2GA4UTHxhT0chKHdIKeDLmTm0VSkfNeOhQIkW7vB
|
|
S/WT9y78319QJek8OKwJo0Jv0O93rvZZI0JFjXGtP15XNBfObMtPXn3l8qoLzhsv
|
|
pJJG/u+BsVZ+y1JDQQlHaD1P2TLW/nGymFq12k693IOCmNyaIOa01Wa9B/j3a3RY
|
|
v4SdkULvJKbttNMNBgIMJ74wZp5EUhEFs68sllrIrmthH8bW2fbcHEQ1g/MJCe3+
|
|
43c9aoW8yNQmuEe7yre9lgqcJOIOxlb5XEJhH0Lh+8OBi5aHA/5wXGU5WrhWqHCR
|
|
npXBsNqy2sKUuVkEzvn3Hd6aoKncVLrgNR8xA3VP86jJhawvO+M+YYMr1wOVHc/I
|
|
PYq9hlyUR8qJ/0RpnaIE1iLbPYfEpGTg7oHORpbQVoZAUwMN/Sdox7sMkqCOb1RJ
|
|
Cmy9J5o7iiNOoshvps5cxcbsM7LNfbf0vDhWpckAvsQehrS1mfVuFHkIiotVQhH1
|
|
QXPfvB2cVF/SxMqqHWpnT+8c8klfS03kXSb0BdknrQ4DNPq1H5A=
|
|
=3A1s
|
|
-----END PGP SIGNATURE-----`
|
|
|
|
// testOtherKeyTrustSignatureArmor is a trust signature of another key (not the
|
|
// author key), signed with HashicorpPartnersKey.
|
|
const testOtherKeyTrustSignatureArmor = `-----BEGIN PGP SIGNATURE-----
|
|
|
|
iQIzBAABCAAdFiEEUYkGV8Ws20uCMIZWfXLUJo5GYPwFAl6POvsACgkQfXLUJo5G
|
|
YPyGihAAomM1kGmrC5KRgWQ+V47r8wFoIkhsTgAYb9ENOzn/RVJt3SJSstcKxfA3
|
|
7HW5R4kqAoXH1hcPYpUcOcdeAvtZxjGRQ9JgErV8NBg6sR11aQccCzAG4Hy0hWav
|
|
/jB5NzTEX5JFEXH6WhpWI1avh0l2j6JxO1K1s+5+5PI3KbuO+XSqeZ3QmUz9FwGu
|
|
pr0J6oYcERupzrpnmgMb5fbkpHfzffR2/MOYdF9Hae4EvDS1b7tokuuKsStNnCm0
|
|
ge7PFdekwbj/OiQrQlqM1pOw2siPX3ouWCtW8oExm9tAxNw31Bn2g3oaNMkHMqJd
|
|
hlVUZlqeJMyylUat3cY7GTQONfCnoyUHe/wv8exBUbV3v2glp9y2g9i2XmXkHOrV
|
|
Z+pnNBc+jdp3a4O0Y8fXXZdjiIolZKY8BbvzheuMrQQIOmw4N3KrZbTpLKuqz8rb
|
|
h8bqUbU42oWcJmBvzF4NZ4tQ+aFHs4CbOnjfDfS14baQr2Gqo9BqTfrzS5Pbs8lq
|
|
AhY0r+zi71lQ1rBfgZfjd8zWlOzpDO//nwKhGCqYOWke/C/T6o0zxM0R4uR4zXwT
|
|
KhvXK8/kK/L8Flaxqme0d5bzXLbsMe9I6I76DY5iNhkiFnnWt4+FhGoIDR03MTKS
|
|
SnHodBLlpKLyUXi36DCDy/iKVsieqLsAdcYe0nQFuhoQcOme33A=
|
|
=aHOG
|
|
-----END PGP SIGNATURE-----`
|
|
|
|
// 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
|
|
// a release.
|
|
const testAuthorSignatureGoodBase64 = `iQEzBAABCAAdFiEEW/7sQxfnRgCGIZcGN6arO88s` +
|
|
`FwoFAl5vh7gACgkQN6arO88sFwrAlQf6Al77qzjxNIj+NQNJfBGYUE5jHIgcuWOs1IPRTYUI` +
|
|
`rHQIUU2RVrdHoAefKTKNzGde653JK/pYTflSV+6ini3/aZZnXlF6t001w3wswmakdwTr0hXx` +
|
|
`Ez/hHYio72Gpn7+T/L+nl6dKkjeGqd/Kor5x2TY9uYB737ESmAe5T8ZlPaGMFHh0mYlNTeRq` +
|
|
`4qIKqL6DwddBF4Ju2svn2MeNMGfE358H31mxAl2k4PPrwBTR1sFUCUOzAXVA/g9Ov5Y9ni2G` +
|
|
`rkTahBtV9yuUUd1D+oRTTTdP0bj3A+3xxXmKTBhRuvurydPTicKuWzeILIJkcwp7Kl5UbI2N` +
|
|
`n1ayZdaCIw/r4w==`
|
|
|
|
// testSignatureBadBase64 is an invalid signature.
|
|
const testSignatureBadBase64 = `iQEzBAABCAAdFiEEW/7sQxfnRgCGIZcGN6arO88s` +
|
|
`4qIKqL6DwddBF4Ju2svn2MeNMGfE358H31mxAl2k4PPrwBTR1sFUCUOzAXVA/g9Ov5Y9ni2G` +
|
|
`rkTahBtV9yuUUd1D+oRTTTdP0bj3A+3xxXmKTBhRuvurydPTicKuWzeILIJkcwp7Kl5UbI2N` +
|
|
`n1ayZdaCIw/r4w==`
|
|
|
|
// testHashiCorpPublicKeyID is the Key ID of the HashiCorpPublicKey.
|
|
const testHashiCorpPublicKeyID = `34365D9472D7468F`
|
|
|
|
const testProviderShaSums = `fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e terraform-provider-null_3.1.0_darwin_amd64.zip
|
|
9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2 terraform-provider-null_3.1.0_darwin_arm64.zip
|
|
a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e terraform-provider-null_3.1.0_freebsd_386.zip
|
|
5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521 terraform-provider-null_3.1.0_freebsd_amd64.zip
|
|
fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b terraform-provider-null_3.1.0_freebsd_arm.zip
|
|
c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d terraform-provider-null_3.1.0_linux_386.zip
|
|
53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515 terraform-provider-null_3.1.0_linux_amd64.zip
|
|
cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8 terraform-provider-null_3.1.0_linux_arm64.zip
|
|
e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70 terraform-provider-null_3.1.0_linux_arm.zip
|
|
a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53 terraform-provider-null_3.1.0_windows_386.zip
|
|
02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2 terraform-provider-null_3.1.0_windows_amd64.zip
|
|
`
|
|
|
|
// testHashicorpSignatureGoodBase64 is a signature of testProviderShaSums signed with
|
|
// HashicorpPublicKey, which represents the SHA256SUMS.sig file downloaded for
|
|
// an official release.
|
|
const testHashicorpSignatureGoodBase64 = `wsFcBAABCAAQBQJgga+GCRCwtEEJdoW2dgAA` +
|
|
`o0YQAAW911BGDr2WHLo5NwcZenwHyxL5DX9g+4BknKbc/WxRC1hD8Afi3eygZk1yR6eT4Gp2H` +
|
|
`yNOwCjGL1PTONBumMfj9udIeuX8onrJMMvjFHh+bORGxBi4FKr4V3b2ZV1IYOjWMEyyTGRDvw` +
|
|
`SCdxBkp3apH3s2xZLmRoAj84JZ4KaxGF7hlT0j4IkNyQKd2T5cCByN9DV80+x+HtzaOieFwJL` +
|
|
`97iyGj6aznXfKfslK6S4oIrVTwyLTrQbxSxA0LsdUjRPHnJamL3sFOG77qUEUoXG3r61yi5vW` +
|
|
`V4P5gCH/+C+VkfGHqaB1s0jHYLxoTEXtwthe66MydDBPe2Hd0J12u9ppOIeK3leeb4uiixWIi` +
|
|
`rNdpWyjr/LU1KKWPxsDqMGYJ9TexyWkXjEpYmIEiY1Rxar8jrLh+FqVAhxRJajjgSRu5pZj50` +
|
|
`CNeKmmbyolLhPCmICjYYU/xKPGXSyDFqonVVyMWCSpO+8F38OmwDQHIk5AWyc8hPOAZ+g5N95` +
|
|
`cfUAzEqlvmNvVHQIU40Y6/Ip2HZzzFCLKQkMP1aDakYHq5w4ZO/ucjhKuoh1HDQMuMnZSu4eo` +
|
|
`2nMTBzYZnUxwtROrJZF1t103avbmP2QE/GaPvLIQn7o5WMV3ZcPCJ+szzzby7H2e33WIynrY/` +
|
|
`95ensBxh7mGFbcQ1C59b5o7viwIaaY2`
|
|
|
|
// entityString function is used for logging the signing key.
|
|
func TestEntityString(t *testing.T) {
|
|
var tests = []struct {
|
|
name string
|
|
entity *openpgp.Entity
|
|
expected string
|
|
}{
|
|
{
|
|
"nil",
|
|
nil,
|
|
"",
|
|
},
|
|
{
|
|
"testAuthorKeyArmor",
|
|
testReadArmoredEntity(t, testAuthorKeyArmor),
|
|
"37A6AB3BCF2C170A Terraform Testing (plugin/discovery/) <terraform+testing@hashicorp.com>",
|
|
},
|
|
{
|
|
"HashicorpPublicKey",
|
|
testReadArmoredEntity(t, HashicorpPublicKey),
|
|
"34365D9472D7468F HashiCorp Security (hashicorp.com/security) <security@hashicorp.com>",
|
|
},
|
|
{
|
|
"HashicorpPartnersKey",
|
|
testReadArmoredEntity(t, HashicorpPartnersKey),
|
|
"7D72D4268E4660FC HashiCorp Security (Terraform Partner Signing) <security+terraform@hashicorp.com>",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
actual := entityString(tt.entity)
|
|
if actual != tt.expected {
|
|
t.Errorf("expected %s, actual %s", tt.expected, actual)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testReadArmoredEntity(t *testing.T, armor string) *openpgp.Entity {
|
|
data := strings.NewReader(armor)
|
|
|
|
el, err := openpgp.ReadArmoredKeyRing(data)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if count := len(el); count != 1 {
|
|
t.Fatalf("expected 1 entity, got %d", count)
|
|
}
|
|
|
|
return el[0]
|
|
}
|