1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
351
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