...

Source file src/github.com/sigstore/rekor/pkg/pki/pgp/pgp_test.go

Documentation: github.com/sigstore/rekor/pkg/pki/pgp

     1  //
     2  // Copyright 2021 The Sigstore Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package pgp
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"errors"
    22  	"io"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"os"
    26  	"reflect"
    27  	"sort"
    28  	"testing"
    29  
    30  	"go.uber.org/goleak"
    31  )
    32  
    33  func TestMain(m *testing.M) {
    34  	goleak.VerifyTestMain(m)
    35  }
    36  
    37  func TestReadPublicKey(t *testing.T) {
    38  	type test struct {
    39  		caseDesc   string
    40  		inputFile  string
    41  		errorFound bool
    42  	}
    43  
    44  	tests := []test{
    45  		{caseDesc: "Not a valid armored public key file", inputFile: "testdata/hello_world.txt.asc.sig", errorFound: true},
    46  		{caseDesc: "Armored private key (should fail)", inputFile: "testdata/armored_private.pgp", errorFound: true},
    47  		{caseDesc: "Valid armored public key", inputFile: "testdata/valid_armored_public.pgp", errorFound: false},
    48  		{caseDesc: "Valid armored public key with multiple subentries", inputFile: "testdata/valid_armored_complex_public.pgp", errorFound: false},
    49  		{caseDesc: "Not a valid binary public key", inputFile: "testdata/bogus_binary.pgp", errorFound: true},
    50  		{caseDesc: "Binary private key (should fail)", inputFile: "testdata/binary_private.pgp", errorFound: true},
    51  		{caseDesc: "Valid binary public key", inputFile: "testdata/valid_binary_public.pgp", errorFound: false},
    52  		{caseDesc: "Valid binary public key with multiple subentries", inputFile: "testdata/valid_binary_complex_public.pgp", errorFound: false},
    53  	}
    54  
    55  	for _, tc := range tests {
    56  		file, err := os.Open(tc.inputFile)
    57  		if err != nil {
    58  			t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile)
    59  		}
    60  
    61  		if got, err := NewPublicKey(file); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) {
    62  			t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, err)
    63  		}
    64  	}
    65  }
    66  
    67  func TestReadSignature(t *testing.T) {
    68  	type test struct {
    69  		caseDesc   string
    70  		inputFile  string
    71  		errorFound bool
    72  	}
    73  
    74  	tests := []test{
    75  		{caseDesc: "Not a valid signature file", inputFile: "testdata/bogus_armored.pgp", errorFound: true},
    76  		{caseDesc: "Invalid armored signature", inputFile: "testdata/valid_armored_public.pgp", errorFound: true},
    77  		{caseDesc: "Valid armored signature", inputFile: "testdata/hello_world.txt.asc.sig", errorFound: false},
    78  		{caseDesc: "Valid binary signature", inputFile: "testdata/hello_world.txt.sig", errorFound: false},
    79  		{caseDesc: "Valid armored V3 signature", inputFile: "testdata/hello_world.txt.asc.v3.sig", errorFound: false},
    80  		{caseDesc: "Valid binary V3 signature", inputFile: "testdata/hello_world.txt.v3.sig", errorFound: false},
    81  	}
    82  
    83  	for _, tc := range tests {
    84  		file, err := os.Open(tc.inputFile)
    85  		if err != nil {
    86  			t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile)
    87  		}
    88  		if got, err := NewSignature(file); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) {
    89  			t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, got)
    90  		}
    91  	}
    92  }
    93  
    94  type BadReader struct {
    95  }
    96  
    97  func (br BadReader) Read(_ []byte) (n int, err error) {
    98  	return 0, errors.New("test error")
    99  }
   100  
   101  func TestReadErrorPublicKey(t *testing.T) {
   102  	br := new(BadReader)
   103  	if _, err := NewPublicKey(br); err == nil {
   104  		t.Errorf("KnownBadReader: unexpected success testing a broken reader for public key")
   105  	}
   106  }
   107  
   108  func TestReadErrorSignature(t *testing.T) {
   109  	br := new(BadReader)
   110  	if _, err := NewSignature(br); err == nil {
   111  		t.Errorf("KnownBadReader: unexpected success testing a broken reader for signature")
   112  	}
   113  }
   114  
   115  func TestFetchPublicKey(t *testing.T) {
   116  	type test struct {
   117  		caseDesc   string
   118  		inputFile  string
   119  		errorFound bool
   120  	}
   121  
   122  	testServer := httptest.NewServer(http.HandlerFunc(
   123  		func(w http.ResponseWriter, r *http.Request) {
   124  			if r.URL.Path[1:] == "premature_close" {
   125  				return
   126  			}
   127  
   128  			file, err := os.ReadFile(r.URL.Path[1:])
   129  			if err != nil {
   130  				w.WriteHeader(http.StatusNotFound)
   131  				return
   132  			}
   133  			w.WriteHeader(http.StatusOK)
   134  			_, _ = w.Write(file)
   135  		}))
   136  	defer testServer.Close()
   137  
   138  	tests := []test{
   139  		{caseDesc: "Not a valid URL", inputFile: "%invalid_url%", errorFound: true},
   140  		{caseDesc: "HTTP server prematurely closes transaction", inputFile: "premature_close", errorFound: true},
   141  		{caseDesc: "404 error fetching content", inputFile: "not_a_file", errorFound: true},
   142  		{caseDesc: "Invalid public key", inputFile: "testdata/bogus_armored.pgp", errorFound: true},
   143  		{caseDesc: "Private key (should fail)", inputFile: "testdata/armored_private.pgp", errorFound: true},
   144  		{caseDesc: "Valid armored public key", inputFile: "testdata/valid_armored_public.pgp", errorFound: false},
   145  		{caseDesc: "Valid armored public key with multiple subentries", inputFile: "testdata/valid_armored_complex_public.pgp", errorFound: false},
   146  	}
   147  
   148  	for _, tc := range tests {
   149  		if got, err := FetchPublicKey(context.TODO(), testServer.URL+"/"+tc.inputFile); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) {
   150  			t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, got)
   151  		}
   152  	}
   153  }
   154  
   155  func TestFetchSignature(t *testing.T) {
   156  	type test struct {
   157  		caseDesc   string
   158  		inputFile  string
   159  		errorFound bool
   160  	}
   161  
   162  	testServer := httptest.NewServer(http.HandlerFunc(
   163  		func(w http.ResponseWriter, r *http.Request) {
   164  			if r.URL.Path[1:] == "premature_close" {
   165  				return
   166  			}
   167  
   168  			file, err := os.ReadFile(r.URL.Path[1:])
   169  			if err != nil {
   170  				w.WriteHeader(http.StatusNotFound)
   171  				return
   172  			}
   173  			w.WriteHeader(http.StatusOK)
   174  			_, _ = w.Write(file)
   175  		}))
   176  	defer testServer.Close()
   177  
   178  	tests := []test{
   179  		{caseDesc: "Not a valid URL", inputFile: "%invalid_url%", errorFound: true},
   180  		{caseDesc: "HTTP server prematurely closes transaction", inputFile: "premature_close", errorFound: true},
   181  		{caseDesc: "404 error fetching content", inputFile: "not_a_file", errorFound: true},
   182  		{caseDesc: "Invalid signature", inputFile: "testdata/bogus_armored.pgp", errorFound: true},
   183  		{caseDesc: "Valid armored signature", inputFile: "testdata/hello_world.txt.asc.sig", errorFound: false},
   184  		{caseDesc: "Valid binary signature", inputFile: "testdata/hello_world.txt.sig", errorFound: false},
   185  	}
   186  
   187  	for _, tc := range tests {
   188  		if got, err := FetchSignature(context.TODO(), testServer.URL+"/"+tc.inputFile); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) {
   189  			t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, got)
   190  		}
   191  	}
   192  }
   193  
   194  func TestCanonicalValueSignature(t *testing.T) {
   195  	type test struct {
   196  		caseDesc      string
   197  		inputFile     string
   198  		keyFile       string
   199  		sigFile       string
   200  		expectSuccess bool
   201  	}
   202  
   203  	var s Signature
   204  	if _, err := s.CanonicalValue(); err == nil {
   205  		t.Errorf("CanonicalValue did not error out for uninitialized signature")
   206  	}
   207  
   208  	tests := []test{
   209  		{
   210  			caseDesc:      "Binary signature and canonicalized (armored) signature both verify the same file",
   211  			inputFile:     "testdata/hello_world.txt",
   212  			keyFile:       "testdata/valid_armored_public.pgp",
   213  			sigFile:       "testdata/hello_world.txt.sig",
   214  			expectSuccess: true,
   215  		},
   216  		{
   217  			caseDesc:      "Armored signature and canonicalized (armored) signature both verify the same file",
   218  			inputFile:     "testdata/hello_world.txt",
   219  			keyFile:       "testdata/valid_armored_public.pgp",
   220  			sigFile:       "testdata/hello_world.txt.asc.sig",
   221  			expectSuccess: true,
   222  		},
   223  		{
   224  			caseDesc:      "Binary V3 signature and canonicalized (armored) signature both verify the same file",
   225  			inputFile:     "testdata/hello_world.txt",
   226  			keyFile:       "testdata/valid_armored_public.pgp",
   227  			sigFile:       "testdata/hello_world.txt.v3.sig",
   228  			expectSuccess: true,
   229  		},
   230  		{
   231  			caseDesc:      "Armored V3 signature and canonicalized (armored) signature both verify the same file",
   232  			inputFile:     "testdata/hello_world.txt",
   233  			keyFile:       "testdata/valid_armored_public.pgp",
   234  			sigFile:       "testdata/hello_world.txt.asc.v3.sig",
   235  			expectSuccess: true,
   236  		},
   237  	}
   238  
   239  	for _, tc := range tests {
   240  		var err error
   241  		inputFile, err := os.Open(tc.inputFile)
   242  		if err != nil {
   243  			t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile)
   244  		}
   245  
   246  		sigFile, err := os.Open(tc.sigFile)
   247  		if err != nil {
   248  			t.Errorf("%v: cannot open %v", tc.caseDesc, tc.sigFile)
   249  		}
   250  
   251  		keyFile, err := os.Open(tc.keyFile)
   252  		if err != nil {
   253  			t.Errorf("%v: cannot open %v", tc.caseDesc, tc.keyFile)
   254  		}
   255  
   256  		key, err := NewPublicKey(keyFile)
   257  		if err != nil {
   258  			t.Errorf("%v: Error reading public key for TestCanonicalValueSignature: %v", tc.caseDesc, err)
   259  		}
   260  
   261  		sig, err := NewSignature(sigFile)
   262  		if err != nil {
   263  			t.Errorf("%v: Error reading signature for TestCanonicalValueSignature: %v", tc.caseDesc, err)
   264  		}
   265  
   266  		if err := sig.Verify(inputFile, key); err != nil {
   267  			t.Errorf("%v: Error verifying pre-canonicalized signature for TestCanonicalValueSignature: %v", tc.caseDesc, err)
   268  		}
   269  
   270  		canonicalSigBytes, err := sig.CanonicalValue()
   271  		if err != nil {
   272  			t.Errorf("%v: Error canonicalizing signature '%v': %v", tc.caseDesc, tc.sigFile, err)
   273  		}
   274  
   275  		canonicalSig, err := NewSignature(bytes.NewReader(canonicalSigBytes))
   276  		if err != nil {
   277  			t.Errorf("%v: Error reading canonicalized signature for TestCanonicalValueSignature: %v", tc.caseDesc, err)
   278  		}
   279  
   280  		_, _ = inputFile.Seek(0, io.SeekStart)
   281  
   282  		if err := canonicalSig.Verify(inputFile, key); (err == nil) != tc.expectSuccess {
   283  			t.Errorf("%v: canonical signature was unable to be verified: %v", tc.caseDesc, err)
   284  		}
   285  	}
   286  }
   287  
   288  func TestCanonicalValuePublicKey(t *testing.T) {
   289  	type test struct {
   290  		caseDesc string
   291  		input    string
   292  		output   string
   293  		match    bool
   294  	}
   295  
   296  	var k PublicKey
   297  	if _, err := k.CanonicalValue(); err == nil {
   298  		t.Errorf("CanonicalValue did not error out for uninitialized key")
   299  	}
   300  
   301  	tests := []test{
   302  		{caseDesc: "Binary and Armored versions of same key", input: "testdata/valid_binary_public.pgp", output: "testdata/valid_armored_public.pgp", match: true},
   303  		{caseDesc: "Complex binary and armored versions of same key", input: "testdata/valid_binary_complex_public.pgp", output: "testdata/valid_armored_complex_public.pgp", match: true},
   304  	}
   305  
   306  	for _, tc := range tests {
   307  		var inputFile, outputFile io.Reader
   308  		var err error
   309  		inputFile, err = os.Open(tc.input)
   310  		if err != nil {
   311  			t.Errorf("%v: cannot open %v", tc.caseDesc, tc.input)
   312  		}
   313  
   314  		inputKey, err := NewPublicKey(inputFile)
   315  		if err != nil {
   316  			t.Errorf("%v: Error reading input for TestCanonicalValuePublicKey: %v", tc.caseDesc, err)
   317  		}
   318  
   319  		cvInput, err := inputKey.CanonicalValue()
   320  		if err != nil {
   321  			t.Errorf("%v: Error canonicalizing public key '%v': %v", tc.caseDesc, tc.input, err)
   322  		}
   323  
   324  		outputFile, err = os.Open(tc.output)
   325  		if err != nil {
   326  			t.Errorf("%v: cannot open %v", tc.caseDesc, tc.output)
   327  		}
   328  
   329  		outputKey, err := NewPublicKey(outputFile)
   330  		if err != nil {
   331  			t.Errorf("%v: Error reading input for TestCanonicalValuePublicKey: %v", tc.caseDesc, err)
   332  		}
   333  
   334  		cvOutput, err := outputKey.CanonicalValue()
   335  		if err != nil {
   336  			t.Errorf("%v: Error canonicalizing public key '%v': %v", tc.caseDesc, tc.input, err)
   337  		}
   338  
   339  		if bytes.Equal(cvInput, cvOutput) != tc.match {
   340  			t.Errorf("%v: %v equality of canonical values of %v and %v was expected but not generated", tc.caseDesc, tc.match, tc.input, tc.output)
   341  		}
   342  	}
   343  }
   344  
   345  func TestEmailAddresses(t *testing.T) {
   346  	type test struct {
   347  		caseDesc  string
   348  		inputFile string
   349  		subjects  []string
   350  		// number of keys in key ring
   351  		// verified with gpg, ignoring DSA/ElGamal keys
   352  		keys int
   353  	}
   354  
   355  	var k PublicKey
   356  	if len(k.Subjects()) != 0 {
   357  		t.Errorf("Subjects for unitialized key should give empty slice")
   358  	}
   359  	tests := []test{
   360  		{caseDesc: "Valid armored public key", inputFile: "testdata/valid_armored_public.pgp", subjects: []string{}, keys: 2},
   361  		{caseDesc: "Valid armored public key with multiple subentries", inputFile: "testdata/valid_armored_complex_public.pgp", subjects: []string{"linux-packages-keymaster@google.com", "linux-packages-keymaster@google.com"}, keys: 4},
   362  		{caseDesc: "Valid binary public key", inputFile: "testdata/valid_binary_public.pgp", subjects: []string{}, keys: 2},
   363  		{caseDesc: "Valid binary public key with multiple subentries", inputFile: "testdata/valid_binary_complex_public.pgp", subjects: []string{"linux-packages-keymaster@google.com", "linux-packages-keymaster@google.com"}, keys: 4},
   364  	}
   365  
   366  	for _, tc := range tests {
   367  		var input io.Reader
   368  		var err error
   369  		input, err = os.Open(tc.inputFile)
   370  		if err != nil {
   371  			t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile)
   372  		}
   373  
   374  		inputKey, err := NewPublicKey(input)
   375  		if err != nil {
   376  			t.Errorf("%v: Error reading input for TestEmailAddresses: %v", tc.caseDesc, err)
   377  		}
   378  
   379  		subjects := inputKey.Subjects()
   380  
   381  		if len(subjects) == len(tc.subjects) {
   382  			if len(subjects) > 0 {
   383  				sort.Strings(subjects)
   384  				sort.Strings(tc.subjects)
   385  				if !reflect.DeepEqual(subjects, tc.subjects) {
   386  					t.Errorf("%v: Error getting subjects from keys, got %v, expected %v", tc.caseDesc, subjects, tc.subjects)
   387  				}
   388  			}
   389  		} else {
   390  			t.Errorf("%v: Error getting subjects from keys length, got %v, expected %v", tc.caseDesc, len(subjects), len(tc.subjects))
   391  		}
   392  
   393  		ids, err := inputKey.Identities()
   394  		if err != nil {
   395  			t.Fatalf("%v: unexpected error getting identities: %v", tc.caseDesc, err)
   396  		}
   397  		if len(ids) != tc.keys {
   398  			t.Fatalf("%v: expected %d keys, got %d", tc.caseDesc, tc.keys, len(ids))
   399  		}
   400  	}
   401  }
   402  
   403  func TestVerifySignature(t *testing.T) {
   404  	type test struct {
   405  		caseDesc string
   406  		dataFile string
   407  		sigFile  string
   408  		keyFile  string
   409  		verified bool
   410  	}
   411  
   412  	tests := []test{
   413  		{caseDesc: "Valid Armored Signature, Armored Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.asc.sig", keyFile: "testdata/valid_armored_public.pgp", verified: true},
   414  		{caseDesc: "Valid Armored Signature, Binary Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.asc.sig", keyFile: "testdata/valid_binary_public.pgp", verified: true},
   415  		{caseDesc: "Valid Binary Signature, Armored Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.sig", keyFile: "testdata/valid_armored_public.pgp", verified: true},
   416  		{caseDesc: "Valid Binary Signature, Binary Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.sig", keyFile: "testdata/valid_binary_public.pgp", verified: true},
   417  		{caseDesc: "Valid V3 Armored Signature, Armored Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.asc.v3.sig", keyFile: "testdata/valid_armored_public.pgp", verified: true},
   418  		{caseDesc: "Valid V3 Armored Signature, Binary Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.asc.v3.sig", keyFile: "testdata/valid_binary_public.pgp", verified: true},
   419  		{caseDesc: "Valid V3 Binary Signature, Armored Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.v3.sig", keyFile: "testdata/valid_armored_public.pgp", verified: true},
   420  		{caseDesc: "Valid V3 Binary Signature, Binary Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.v3.sig", keyFile: "testdata/valid_binary_public.pgp", verified: true},
   421  		{caseDesc: "Valid Signature, Incorrect Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.sig", keyFile: "testdata/valid_binary_complex_public.pgp", verified: false},
   422  		{caseDesc: "Data does not match Signature", dataFile: "testdata/armored_private.pgp", sigFile: "testdata/hello_world.txt.sig", keyFile: "testdata/valid_binary_complex_public.pgp", verified: false},
   423  	}
   424  
   425  	for _, tc := range tests {
   426  		keyFile, err := os.Open(tc.keyFile)
   427  		if err != nil {
   428  			t.Errorf("%v: error reading keyfile '%v': %v", tc.caseDesc, tc.keyFile, err)
   429  		}
   430  		k, err := NewPublicKey(keyFile)
   431  		if err != nil {
   432  			t.Errorf("%v: error reading keyfile '%v': %v", tc.caseDesc, tc.keyFile, err)
   433  		}
   434  
   435  		sigFile, err := os.Open(tc.sigFile)
   436  		if err != nil {
   437  			t.Errorf("%v: error reading sigfile '%v': %v", tc.caseDesc, tc.sigFile, err)
   438  		}
   439  		s, err := NewSignature(sigFile)
   440  		if err != nil {
   441  			t.Errorf("%v: error reading sigfile '%v': %v", tc.caseDesc, tc.sigFile, err)
   442  		}
   443  
   444  		dataFile, err := os.Open(tc.dataFile)
   445  		if err != nil {
   446  			t.Errorf("%v: error reading datafile '%v': %v", tc.caseDesc, tc.dataFile, err)
   447  		}
   448  
   449  		if err := s.Verify(dataFile, k); (err == nil) != tc.verified {
   450  			t.Errorf("%v: unexpected result in verifying sigature: %v", tc.caseDesc, err)
   451  		}
   452  	}
   453  
   454  	emptyKey := PublicKey{}
   455  	emptySig := Signature{}
   456  
   457  	if err := emptySig.Verify(bytes.NewReader([]byte("irrelevant")), emptyKey); err == nil {
   458  		t.Errorf("expected error when using empty sig to verify")
   459  	}
   460  
   461  	sigFile, _ := os.Open("testdata/hello_world.txt.sig")
   462  	validSig, _ := NewSignature(sigFile)
   463  
   464  	if err := validSig.Verify(bytes.NewReader([]byte("irrelevant")), &emptyKey); err == nil {
   465  		t.Errorf("expected error when using empty key to verify")
   466  	}
   467  }
   468  

View as plain text