1package transit
2
3import (
4	"context"
5	"encoding/base64"
6	"fmt"
7	"strconv"
8	"strings"
9	"testing"
10
11	"golang.org/x/crypto/ed25519"
12
13	"github.com/hashicorp/vault/sdk/helper/keysutil"
14	"github.com/hashicorp/vault/sdk/logical"
15	"github.com/mitchellh/mapstructure"
16)
17
18// The outcome of processing a request includes
19// the possibility that the request is incomplete or incorrect,
20// or that the request is well-formed but the signature (for verification)
21// is invalid, or that the signature is valid, but the key is not.
22type signOutcome struct {
23	requestOk bool
24	valid     bool
25	keyValid  bool
26}
27
28func TestTransit_SignVerify_ECDSA(t *testing.T) {
29	t.Run("256", func(t *testing.T) {
30		testTransit_SignVerify_ECDSA(t, 256)
31	})
32	t.Run("384", func(t *testing.T) {
33		testTransit_SignVerify_ECDSA(t, 384)
34	})
35	t.Run("521", func(t *testing.T) {
36		testTransit_SignVerify_ECDSA(t, 521)
37	})
38}
39
40func testTransit_SignVerify_ECDSA(t *testing.T, bits int) {
41	b, storage := createBackendWithSysView(t)
42
43	// First create a key
44	req := &logical.Request{
45		Storage:   storage,
46		Operation: logical.UpdateOperation,
47		Path:      "keys/foo",
48		Data: map[string]interface{}{
49			"type": fmt.Sprintf("ecdsa-p%d", bits),
50		},
51	}
52	_, err := b.HandleRequest(context.Background(), req)
53	if err != nil {
54		t.Fatal(err)
55	}
56
57	// Now, change the key value to something we control
58	p, _, err := b.lm.GetPolicy(context.Background(), keysutil.PolicyRequest{
59		Storage: storage,
60		Name:    "foo",
61	}, b.GetRandomReader())
62	if err != nil {
63		t.Fatal(err)
64	}
65
66	// Useful code to output a key for openssl verification
67	/*
68		if bits == 384 {
69			var curve elliptic.Curve
70			switch bits {
71			case 521:
72				curve = elliptic.P521()
73			case 384:
74				curve = elliptic.P384()
75			default:
76				curve = elliptic.P256()
77			}
78			key := p.Keys[strconv.Itoa(p.LatestVersion)]
79			keyBytes, _ := x509.MarshalECPrivateKey(&ecdsa.PrivateKey{
80				PublicKey: ecdsa.PublicKey{
81					Curve: curve,
82					X:     key.EC_X,
83					Y:     key.EC_Y,
84				},
85				D: key.EC_D,
86			})
87			pemBlock := &pem.Block{
88				Type:  "EC PRIVATE KEY",
89				Bytes: keyBytes,
90			}
91			pemBytes := pem.EncodeToMemory(pemBlock)
92			t.Fatalf("X: %s, Y: %s, D: %s, marshaled: %s", key.EC_X.Text(16), key.EC_Y.Text(16), key.EC_D.Text(16), string(pemBytes))
93		}
94	*/
95
96	var xString, yString, dString string
97	switch bits {
98	case 384:
99		xString = "703457a84e48bfcb037cfb509f1870d2aa5b74c109c2f24624ab21444492575229f8711453e5c656dab596b4e26db30e"
100		yString = "411c5b7092a893dc8b7af39de3d21d1c26f45b27616baeac4c479ef3c9f21c194b5ac501dee47ba2b2cb243a54256524"
101		dString = "3de3e4fd2ecbc490e956f41f5003a1e57a84763cec7b722fa3427cf461a1148ea4d5206023bcce0422289f6633730759"
102		/*
103			-----BEGIN EC PRIVATE KEY-----
104			MIGkAgEBBDA94+T9LsvEkOlW9B9QA6HleoR2POx7ci+jQnz0YaEUjqTVIGAjvM4E
105			IiifZjNzB1mgBwYFK4EEACKhZANiAARwNFeoTki/ywN8+1CfGHDSqlt0wQnC8kYk
106			qyFERJJXUin4cRRT5cZW2rWWtOJtsw5BHFtwkqiT3It6853j0h0cJvRbJ2FrrqxM
107			R57zyfIcGUtaxQHe5HuissskOlQlZSQ=
108			-----END EC PRIVATE KEY-----
109		*/
110	case 521:
111		xString = "1913f75fc044fe5d1f871c2629a377462fd819b174a41d3ec7d04ebd5ae35475ff8de544f4e19a9aa6b16a8f67af479be6884e00ca3147dc24d5924d66ac395e04b"
112		yString = "4919406b90d8323fdb5c9c4f48259c56ebcea37b40ad1a82bbbfad62a9b9c2dce515772274b84725471c7d0b7c62e10c23296b1a9d2b2586ada67735ff5d9fffc4"
113		dString = "1867d0fcd9bac4c5821b70a6b13117499438f8c274579c0aba254fbd85fa98892c3608576197d5534366a9aab0f904155bec46d800d23a57f7f053d91526568b09"
114		/*
115			-----BEGIN EC PRIVATE KEY-----
116			MIHcAgEBBEIAGGfQ/Nm6xMWCG3CmsTEXSZQ4+MJ0V5wKuiVPvYX6mIksNghXYZfV
117			U0Nmqaqw+QQVW+xG2ADSOlf38FPZFSZWiwmgBwYFK4EEACOhgYkDgYYABAGRP3X8
118			BE/l0fhxwmKaN3Ri/YGbF0pB0+x9BOvVrjVHX/jeVE9OGamqaxao9nr0eb5ohOAM
119			oxR9wk1ZJNZqw5XgSwBJGUBrkNgyP9tcnE9IJZxW686je0CtGoK7v61iqbnC3OUV
120			dyJ0uEclRxx9C3xi4QwjKWsanSslhq2mdzX/XZ//xA==
121			-----END EC PRIVATE KEY-----
122		*/
123	default:
124		xString = "7336010a6da5935113d26d9ea4bb61b3b8d102c9a8083ed432f9b58fd7e80686"
125		yString = "4040aa31864691a8a9e7e3ec9250e85425b797ad7be34ba8df62bfbad45ebb0e"
126		dString = "99e5569be8683a2691dfc560ca9dfa71e887867a3af60635a08a3e3655aba3ef"
127	}
128
129	keyEntry := p.Keys[strconv.Itoa(p.LatestVersion)]
130	_, ok := keyEntry.EC_X.SetString(xString, 16)
131	if !ok {
132		t.Fatal("could not set X")
133	}
134	_, ok = keyEntry.EC_Y.SetString(yString, 16)
135	if !ok {
136		t.Fatal("could not set Y")
137	}
138	_, ok = keyEntry.EC_D.SetString(dString, 16)
139	if !ok {
140		t.Fatal("could not set D")
141	}
142	p.Keys[strconv.Itoa(p.LatestVersion)] = keyEntry
143	if err = p.Persist(context.Background(), storage); err != nil {
144		t.Fatal(err)
145	}
146	req.Data = map[string]interface{}{
147		"input": "dGhlIHF1aWNrIGJyb3duIGZveA==",
148	}
149
150	signRequest := func(req *logical.Request, errExpected bool, postpath string) string {
151		t.Helper()
152		req.Path = "sign/foo" + postpath
153		resp, err := b.HandleRequest(context.Background(), req)
154		if err != nil && !errExpected {
155			t.Fatal(err)
156		}
157		if resp == nil {
158			t.Fatal("expected non-nil response")
159		}
160		if errExpected {
161			if !resp.IsError() {
162				t.Fatalf("bad: should have gotten error response: %#v", *resp)
163			}
164			return ""
165		}
166		if resp.IsError() {
167			t.Fatalf("bad: got error response: %#v", *resp)
168		}
169		value, ok := resp.Data["signature"]
170		if !ok {
171			t.Fatalf("no signature key found in returned data, got resp data %#v", resp.Data)
172		}
173		return value.(string)
174	}
175
176	verifyRequest := func(req *logical.Request, errExpected bool, postpath, sig string) {
177		t.Helper()
178		req.Path = "verify/foo" + postpath
179		req.Data["signature"] = sig
180		resp, err := b.HandleRequest(context.Background(), req)
181		if err != nil && !errExpected {
182			t.Fatalf("got error: %v, sig was %v", err, sig)
183		}
184		if errExpected {
185			if resp != nil && !resp.IsError() {
186				t.Fatalf("bad: should have gotten error response: %#v", *resp)
187			}
188			return
189		}
190		if resp == nil {
191			t.Fatal("expected non-nil response")
192		}
193		if resp.IsError() {
194			t.Fatalf("bad: got error response: %#v", *resp)
195		}
196		value, ok := resp.Data["valid"]
197		if !ok {
198			t.Fatalf("no valid key found in returned data, got resp data %#v", resp.Data)
199		}
200		if !value.(bool) && !errExpected {
201			t.Fatalf("verification failed; req was %#v, resp is %#v", *req, *resp)
202		}
203	}
204
205	// Comparisons are against values generated via openssl
206
207	// Test defaults -- sha2-256
208	sig := signRequest(req, false, "")
209	verifyRequest(req, false, "", sig)
210
211	// Test a bad signature
212	verifyRequest(req, true, "", sig[0:len(sig)-2])
213
214	// Test a signature generated with the same key by openssl
215	switch bits {
216	case 384:
217		sig = `vault:v1:MGUCMHHZLRN/3ehWuWACfSCMLtFtNEAdx6Rkwon2Lx6FWCyXCXqH6A8Pz8er0Qkgvm2ElQIxAO922LmUeYzHmDSfC5is/TjFu3b4Fb+1XtoBXncc2u4t2vSuTAxEv7WMh2D2YDdxeA==`
218	case 521:
219		sig = `vault:v1:MIGIAkIBYhspOgSs/K/NUWtlBN+CfYe1IVFpUbQNSqdjT7s+QKcr6GKmdGLIQAXw0q6K0elBgzi1wgLjxwdscwMeW7tm/QQCQgDzdITGlUEd9Z7DOfLCnDP4X8pGsfO60Tvsh/BN44drZsHLtXYBXLczB/XZfIWAsPMuI5F7ExwVNbmQP0FBVri/QQ==`
220	default:
221		sig = `vault:v1:MEUCIAgnEl9V8P305EBAlz68Nq4jZng5fE8k6MactcnlUw9dAiEAvJVePg3dazW6MaW7lRAVtEz82QJDVmR98tXCl8Pc7DA=`
222	}
223	verifyRequest(req, false, "", sig)
224
225	// Test algorithm selection in the path
226	sig = signRequest(req, false, "/sha2-224")
227	verifyRequest(req, false, "/sha2-224", sig)
228
229	// Reset and test algorithm selection in the data
230	req.Data["hash_algorithm"] = "sha2-224"
231	sig = signRequest(req, false, "")
232	verifyRequest(req, false, "", sig)
233
234	req.Data["hash_algorithm"] = "sha2-384"
235	sig = signRequest(req, false, "")
236	verifyRequest(req, false, "", sig)
237
238	req.Data["prehashed"] = true
239	sig = signRequest(req, false, "")
240	verifyRequest(req, false, "", sig)
241	delete(req.Data, "prehashed")
242
243	// Test marshaling selection
244	// Bad value
245	req.Data["marshaling_algorithm"] = "asn2"
246	sig = signRequest(req, true, "")
247	// Use the default, verify we can't validate with jws
248	req.Data["marshaling_algorithm"] = "asn1"
249	sig = signRequest(req, false, "")
250	req.Data["marshaling_algorithm"] = "jws"
251	verifyRequest(req, true, "", sig)
252	// Sign with jws, verify we can validate
253	sig = signRequest(req, false, "")
254	verifyRequest(req, false, "", sig)
255	// If we change marshaling back to asn1 we shouldn't be able to verify
256	delete(req.Data, "marshaling_algorithm")
257	verifyRequest(req, true, "", sig)
258
259	// Test 512 and save sig for later to ensure we can't validate once min
260	// decryption version is set
261	req.Data["hash_algorithm"] = "sha2-512"
262	sig = signRequest(req, false, "")
263	verifyRequest(req, false, "", sig)
264
265	v1sig := sig
266
267	// Test bad algorithm
268	req.Data["hash_algorithm"] = "foobar"
269	signRequest(req, true, "")
270
271	// Test bad input
272	req.Data["hash_algorithm"] = "sha2-256"
273	req.Data["input"] = "foobar"
274	signRequest(req, true, "")
275
276	// Rotate and set min decryption version
277	err = p.Rotate(context.Background(), storage, b.GetRandomReader())
278	if err != nil {
279		t.Fatal(err)
280	}
281	err = p.Rotate(context.Background(), storage, b.GetRandomReader())
282	if err != nil {
283		t.Fatal(err)
284	}
285
286	p.MinDecryptionVersion = 2
287	if err = p.Persist(context.Background(), storage); err != nil {
288		t.Fatal(err)
289	}
290
291	req.Data["input"] = "dGhlIHF1aWNrIGJyb3duIGZveA=="
292	req.Data["hash_algorithm"] = "sha2-256"
293	// Make sure signing still works fine
294	sig = signRequest(req, false, "")
295	verifyRequest(req, false, "", sig)
296	// Now try the v1
297	verifyRequest(req, true, "", v1sig)
298}
299
300func validatePublicKey(t *testing.T, in string, sig string, pubKeyRaw []byte, expectValid bool, postpath string, b *backend) {
301	t.Helper()
302	input, _ := base64.StdEncoding.DecodeString(in)
303	splitSig := strings.Split(sig, ":")
304	signature, _ := base64.StdEncoding.DecodeString(splitSig[2])
305	valid := ed25519.Verify(ed25519.PublicKey(pubKeyRaw), input, signature)
306	if valid != expectValid {
307		t.Fatalf("status of signature: expected %v. Got %v", valid, expectValid)
308	}
309	if !valid {
310		return
311	}
312
313	keyReadReq := &logical.Request{
314		Operation: logical.ReadOperation,
315		Path:      "keys/" + postpath,
316	}
317	keyReadResp, err := b.HandleRequest(context.Background(), keyReadReq)
318	if err != nil {
319		t.Fatal(err)
320	}
321	val := keyReadResp.Data["keys"].(map[string]map[string]interface{})[strings.TrimPrefix(splitSig[1], "v")]
322	var ak asymKey
323	if err := mapstructure.Decode(val, &ak); err != nil {
324		t.Fatal(err)
325	}
326	if ak.PublicKey != "" {
327		t.Fatal("got non-empty public key")
328	}
329	keyReadReq.Data = map[string]interface{}{
330		"context": "abcd",
331	}
332	keyReadResp, err = b.HandleRequest(context.Background(), keyReadReq)
333	if err != nil {
334		t.Fatal(err)
335	}
336	val = keyReadResp.Data["keys"].(map[string]map[string]interface{})[strings.TrimPrefix(splitSig[1], "v")]
337	if err := mapstructure.Decode(val, &ak); err != nil {
338		t.Fatal(err)
339	}
340	if ak.PublicKey != base64.StdEncoding.EncodeToString(pubKeyRaw) {
341		t.Fatalf("got incorrect public key; got %q, expected %q\nasymKey struct is\n%#v", ak.PublicKey, pubKeyRaw, ak)
342	}
343}
344
345func TestTransit_SignVerify_ED25519(t *testing.T) {
346	b, storage := createBackendWithSysView(t)
347
348	// First create a key
349	req := &logical.Request{
350		Storage:   storage,
351		Operation: logical.UpdateOperation,
352		Path:      "keys/foo",
353		Data: map[string]interface{}{
354			"type": "ed25519",
355		},
356	}
357	_, err := b.HandleRequest(context.Background(), req)
358	if err != nil {
359		t.Fatal(err)
360	}
361
362	// Now create a derived key"
363	req = &logical.Request{
364		Storage:   storage,
365		Operation: logical.UpdateOperation,
366		Path:      "keys/bar",
367		Data: map[string]interface{}{
368			"type":    "ed25519",
369			"derived": true,
370		},
371	}
372	_, err = b.HandleRequest(context.Background(), req)
373	if err != nil {
374		t.Fatal(err)
375	}
376
377	// Get the keys for later
378	fooP, _, err := b.lm.GetPolicy(context.Background(), keysutil.PolicyRequest{
379		Storage: storage,
380		Name:    "foo",
381	}, b.GetRandomReader())
382	if err != nil {
383		t.Fatal(err)
384	}
385
386	barP, _, err := b.lm.GetPolicy(context.Background(), keysutil.PolicyRequest{
387		Storage: storage,
388		Name:    "bar",
389	}, b.GetRandomReader())
390	if err != nil {
391		t.Fatal(err)
392	}
393
394	signRequest := func(req *logical.Request, errExpected bool, postpath string) []string {
395		t.Helper()
396		// Delete any key that exists in the request
397		delete(req.Data, "public_key")
398		req.Path = "sign/" + postpath
399		resp, err := b.HandleRequest(context.Background(), req)
400		if err != nil {
401			if !errExpected {
402				t.Fatal(err)
403			}
404			return nil
405		}
406		if resp == nil {
407			t.Fatal("expected non-nil response")
408		}
409		if errExpected {
410			if resp.IsError() {
411				return nil
412			}
413			t.Fatalf("bad: expected error response, got: %#v", *resp)
414		}
415		if resp.IsError() {
416			t.Fatalf("bad: got error response: %#v", *resp)
417		}
418		// memoize any pubic key
419		if key, ok := resp.Data["public_key"]; ok {
420			req.Data["public_key"] = key
421		}
422		// batch_input supplied
423		if _, ok := req.Data["batch_input"]; ok {
424			batchRequestItems := req.Data["batch_input"].([]batchRequestSignItem)
425
426			batchResults, ok := resp.Data["batch_results"]
427			if !ok {
428				t.Fatalf("no batch_results in returned data, got resp data %#v", resp.Data)
429			}
430			batchResponseItems := batchResults.([]batchResponseSignItem)
431			if len(batchResponseItems) != len(batchRequestItems) {
432				t.Fatalf("Expected %d items in response. Got %d: %#v", len(batchRequestItems), len(batchResponseItems), resp)
433			}
434			if len(batchRequestItems) == 0 {
435				return nil
436			}
437			ret := make([]string, len(batchRequestItems))
438			for i, v := range batchResponseItems {
439				ret[i] = v.Signature
440			}
441			return ret
442		}
443
444		// input supplied
445		value, ok := resp.Data["signature"]
446		if !ok {
447			t.Fatalf("no signature key found in returned data, got resp data %#v", resp.Data)
448		}
449		return []string{value.(string)}
450	}
451
452	verifyRequest := func(req *logical.Request, errExpected bool, outcome []signOutcome, postpath string, sig []string, attachSig bool) {
453		t.Helper()
454		req.Path = "verify/" + postpath
455		if _, ok := req.Data["batch_input"]; ok && attachSig {
456			batchRequestItems := req.Data["batch_input"].([]batchRequestSignItem)
457			if len(batchRequestItems) != len(sig) {
458				t.Fatalf("number of requests in batch(%d) != number of signatures(%d)", len(batchRequestItems), len(sig))
459			}
460			for i, v := range sig {
461				batchRequestItems[i]["signature"] = v
462			}
463		} else if attachSig {
464			req.Data["signature"] = sig[0]
465		}
466		resp, err := b.HandleRequest(context.Background(), req)
467		if err != nil && !errExpected {
468			t.Fatalf("got error: %v, sig was %v", err, sig)
469		}
470		if errExpected {
471			if resp != nil && !resp.IsError() {
472				t.Fatalf("bad: expected error response, got: %#v\n%#v", *resp, req)
473			}
474			return
475		}
476		if resp == nil {
477			t.Fatal("expected non-nil response")
478		}
479		if resp.IsError() {
480			t.Fatalf("bad: got error response: %#v", *resp)
481		}
482
483		// batch_input field supplied
484		if _, ok := req.Data["batch_input"]; ok {
485			batchRequestItems := req.Data["batch_input"].([]batchRequestSignItem)
486
487			batchResults, ok := resp.Data["batch_results"]
488			if !ok {
489				t.Fatalf("no batch_results in returned data, got resp data %#v", resp.Data)
490			}
491			batchResponseItems := batchResults.([]batchResponseVerifyItem)
492			if len(batchResponseItems) != len(batchRequestItems) {
493				t.Fatalf("Expected %d items in response. Got %d: %#v", len(batchRequestItems), len(batchResponseItems), resp)
494			}
495			if len(batchRequestItems) == 0 {
496				return
497			}
498			for i, v := range batchResponseItems {
499				if v.Error != "" && outcome[i].requestOk {
500					t.Fatalf("verification failed; req was %#v, resp is %#v", *req, *resp)
501				}
502				if v.Error != "" {
503					continue
504				}
505				if v.Valid != outcome[i].valid {
506					t.Fatalf("verification failed; req was %#v, resp is %#v", *req, *resp)
507				}
508				if !v.Valid {
509					continue
510				}
511				if pubKeyRaw, ok := req.Data["public_key"]; ok {
512					validatePublicKey(t, batchRequestItems[i]["input"], sig[i], pubKeyRaw.([]byte), outcome[i].keyValid, postpath, b)
513				}
514			}
515			return
516		}
517
518		// input field supplied
519		value, ok := resp.Data["valid"]
520		if !ok {
521			t.Fatalf("no valid key found in returned data, got resp data %#v", resp.Data)
522		}
523		valid := value.(bool)
524		if valid != outcome[0].valid {
525			t.Fatalf("verification failed; req was %#v, resp is %#v", *req, *resp)
526		}
527		if !valid {
528			return
529		}
530
531		if pubKeyRaw, ok := req.Data["public_key"]; ok {
532			validatePublicKey(t, req.Data["input"].(string), sig[0], pubKeyRaw.([]byte), outcome[0].keyValid, postpath, b)
533		}
534	}
535
536	req.Data = map[string]interface{}{
537		"input":   "dGhlIHF1aWNrIGJyb3duIGZveA==",
538		"context": "abcd",
539	}
540
541	outcome := []signOutcome{{requestOk: true, valid: true, keyValid: true}}
542	// Test defaults
543	sig := signRequest(req, false, "foo")
544	verifyRequest(req, false, outcome, "foo", sig, true)
545
546	sig = signRequest(req, false, "bar")
547	verifyRequest(req, false, outcome, "bar", sig, true)
548
549	// Verify with incorrect key
550	outcome[0].valid = false
551	verifyRequest(req, false, outcome, "foo", sig, true)
552
553	// Verify with missing signatures
554	delete(req.Data, "signature")
555	verifyRequest(req, true, outcome, "foo", sig, false)
556
557	// Test a bad signature
558	badsig := sig[0]
559	badsig = badsig[:len(badsig)-2]
560	verifyRequest(req, true, outcome, "bar", []string{badsig}, true)
561
562	v1sig := sig
563
564	// Test a missing context
565	delete(req.Data, "context")
566	sig = signRequest(req, true, "bar")
567
568	// Rotate and set min decryption version
569	err = fooP.Rotate(context.Background(), storage, b.GetRandomReader())
570	if err != nil {
571		t.Fatal(err)
572	}
573	err = fooP.Rotate(context.Background(), storage, b.GetRandomReader())
574	if err != nil {
575		t.Fatal(err)
576	}
577	fooP.MinDecryptionVersion = 2
578	if err = fooP.Persist(context.Background(), storage); err != nil {
579		t.Fatal(err)
580	}
581	err = barP.Rotate(context.Background(), storage, b.GetRandomReader())
582	if err != nil {
583		t.Fatal(err)
584	}
585	err = barP.Rotate(context.Background(), storage, b.GetRandomReader())
586	if err != nil {
587		t.Fatal(err)
588	}
589	barP.MinDecryptionVersion = 2
590	if err = barP.Persist(context.Background(), storage); err != nil {
591		t.Fatal(err)
592	}
593
594	req.Data = map[string]interface{}{
595		"input":   "dGhlIHF1aWNrIGJyb3duIGZveA==",
596		"context": "abcd",
597	}
598
599	// Make sure signing still works fine
600	sig = signRequest(req, false, "foo")
601	outcome[0].valid = true
602	verifyRequest(req, false, outcome, "foo", sig, true)
603	// Now try the v1
604	verifyRequest(req, true, outcome, "foo", v1sig, true)
605
606	// Repeat with the other key
607	sig = signRequest(req, false, "bar")
608	verifyRequest(req, false, outcome, "bar", sig, true)
609	verifyRequest(req, true, outcome, "bar", v1sig, true)
610
611	// Test Batch Signing
612	batchInput := []batchRequestSignItem{
613		{"context": "abcd", "input": "dGhlIHF1aWNrIGJyb3duIGZveA=="},
614		{"context": "efgh", "input": "dGhlIHF1aWNrIGJyb3duIGZveA=="},
615	}
616
617	req.Data = map[string]interface{}{
618		"batch_input": batchInput,
619	}
620
621	outcome = []signOutcome{{requestOk: true, valid: true, keyValid: true}, {requestOk: true, valid: true, keyValid: true}}
622
623	sig = signRequest(req, false, "foo")
624	verifyRequest(req, false, outcome, "foo", sig, true)
625
626	goodsig := signRequest(req, false, "bar")
627	verifyRequest(req, false, outcome, "bar", goodsig, true)
628
629	// key doesn't match signatures
630	outcome[0].valid = false
631	outcome[1].valid = false
632	verifyRequest(req, false, outcome, "foo", goodsig, true)
633
634	// Test a bad signature
635	badsig = sig[0]
636	badsig = badsig[:len(badsig)-2]
637	// matching key, but first signature is corrupted
638	outcome[0].requestOk = false
639	outcome[1].valid = true
640	verifyRequest(req, false, outcome, "bar", []string{badsig, goodsig[1]}, true)
641
642	// Verify with missing signatures
643	outcome[0].valid = false
644	outcome[1].valid = false
645	delete(batchInput[0], "signature")
646	delete(batchInput[1], "signature")
647	verifyRequest(req, true, outcome, "foo", sig, false)
648
649	// Test missing context
650	batchInput = []batchRequestSignItem{
651		{"context": "abcd", "input": "dGhlIHF1aWNrIGJyb3duIGZveA=="},
652		{"input": "dGhlIHF1aWNrIGJyb3duIGZveA=="},
653	}
654
655	req.Data = map[string]interface{}{
656		"batch_input": batchInput,
657	}
658
659	sig = signRequest(req, false, "bar")
660
661	outcome[0].requestOk = true
662	outcome[0].valid = true
663	outcome[1].requestOk = false
664	verifyRequest(req, false, outcome, "bar", goodsig, true)
665
666	// Test incorrect context
667	batchInput = []batchRequestSignItem{
668		{"context": "abca", "input": "dGhlIHF1aWNrIGJyb3duIGZveA=="},
669		{"context": "efga", "input": "dGhlIHF1aWNrIGJyb3duIGZveA=="},
670	}
671	req.Data = map[string]interface{}{
672		"batch_input": batchInput,
673	}
674
675	outcome[0].requestOk = true
676	outcome[0].valid = false
677	outcome[1].requestOk = true
678	outcome[1].valid = false
679	verifyRequest(req, false, outcome, "bar", goodsig, true)
680}
681