mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	Add the batch reference field, as in Transform, to Transit operations (#18243)
* Add the batch reference field, as in Transform, to Transit operations * changelog * docs * More mapstructure tags
This commit is contained in:
		| @@ -19,6 +19,10 @@ type DecryptBatchResponseItem struct { | |||||||
| 	// Error, if set represents a failure encountered while encrypting a | 	// Error, if set represents a failure encountered while encrypting a | ||||||
| 	// corresponding batch request item | 	// corresponding batch request item | ||||||
| 	Error string `json:"error,omitempty" structs:"error" mapstructure:"error"` | 	Error string `json:"error,omitempty" structs:"error" mapstructure:"error"` | ||||||
|  |  | ||||||
|  | 	// Reference is an arbitrary caller supplied string value that will be placed on the | ||||||
|  | 	// batch response to ease correlation between inputs and outputs | ||||||
|  | 	Reference string `json:"reference" structs:"reference" mapstructure:"reference"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *backend) pathDecrypt() *framework.Path { | func (b *backend) pathDecrypt() *framework.Path { | ||||||
| @@ -195,6 +199,10 @@ func (b *backend) pathDecryptWrite(ctx context.Context, req *logical.Request, d | |||||||
|  |  | ||||||
| 	resp := &logical.Response{} | 	resp := &logical.Response{} | ||||||
| 	if batchInputRaw != nil { | 	if batchInputRaw != nil { | ||||||
|  | 		// Copy the references | ||||||
|  | 		for i := range batchInputItems { | ||||||
|  | 			batchResponseItems[i].Reference = batchInputItems[i].Reference | ||||||
|  | 		} | ||||||
| 		resp.Data = map[string]interface{}{ | 		resp.Data = map[string]interface{}{ | ||||||
| 			"batch_results": batchResponseItems, | 			"batch_results": batchResponseItems, | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -19,9 +19,9 @@ func TestTransit_BatchDecryption(t *testing.T) { | |||||||
| 	b, s := createBackendWithStorage(t) | 	b, s := createBackendWithStorage(t) | ||||||
|  |  | ||||||
| 	batchEncryptionInput := []interface{}{ | 	batchEncryptionInput := []interface{}{ | ||||||
| 		map[string]interface{}{"plaintext": ""},     // empty string | 		map[string]interface{}{"plaintext": "", "reference": "foo"},     // empty string | ||||||
| 		map[string]interface{}{"plaintext": "Cg=="}, // newline | 		map[string]interface{}{"plaintext": "Cg==", "reference": "bar"}, // newline | ||||||
| 		map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, | 		map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "reference": "baz"}, | ||||||
| 	} | 	} | ||||||
| 	batchEncryptionData := map[string]interface{}{ | 	batchEncryptionData := map[string]interface{}{ | ||||||
| 		"batch_input": batchEncryptionInput, | 		"batch_input": batchEncryptionInput, | ||||||
| @@ -41,7 +41,7 @@ func TestTransit_BatchDecryption(t *testing.T) { | |||||||
| 	batchResponseItems := resp.Data["batch_results"].([]EncryptBatchResponseItem) | 	batchResponseItems := resp.Data["batch_results"].([]EncryptBatchResponseItem) | ||||||
| 	batchDecryptionInput := make([]interface{}, len(batchResponseItems)) | 	batchDecryptionInput := make([]interface{}, len(batchResponseItems)) | ||||||
| 	for i, item := range batchResponseItems { | 	for i, item := range batchResponseItems { | ||||||
| 		batchDecryptionInput[i] = map[string]interface{}{"ciphertext": item.Ciphertext} | 		batchDecryptionInput[i] = map[string]interface{}{"ciphertext": item.Ciphertext, "reference": item.Reference} | ||||||
| 	} | 	} | ||||||
| 	batchDecryptionData := map[string]interface{}{ | 	batchDecryptionData := map[string]interface{}{ | ||||||
| 		"batch_input": batchDecryptionInput, | 		"batch_input": batchDecryptionInput, | ||||||
| @@ -59,7 +59,8 @@ func TestTransit_BatchDecryption(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	batchDecryptionResponseItems := resp.Data["batch_results"].([]DecryptBatchResponseItem) | 	batchDecryptionResponseItems := resp.Data["batch_results"].([]DecryptBatchResponseItem) | ||||||
| 	expectedResult := "[{\"plaintext\":\"\"},{\"plaintext\":\"Cg==\"},{\"plaintext\":\"dGhlIHF1aWNrIGJyb3duIGZveA==\"}]" | 	// This seems fragile | ||||||
|  | 	expectedResult := "[{\"plaintext\":\"\",\"reference\":\"foo\"},{\"plaintext\":\"Cg==\",\"reference\":\"bar\"},{\"plaintext\":\"dGhlIHF1aWNrIGJyb3duIGZveA==\",\"reference\":\"baz\"}]" | ||||||
|  |  | ||||||
| 	jsonResponse, err := json.Marshal(batchDecryptionResponseItems) | 	jsonResponse, err := json.Marshal(batchDecryptionResponseItems) | ||||||
| 	if err != nil || err == nil && string(jsonResponse) != expectedResult { | 	if err != nil || err == nil && string(jsonResponse) != expectedResult { | ||||||
|   | |||||||
| @@ -42,6 +42,10 @@ type BatchRequestItem struct { | |||||||
|  |  | ||||||
| 	// Associated Data for AEAD ciphers | 	// Associated Data for AEAD ciphers | ||||||
| 	AssociatedData string `json:"associated_data" struct:"associated_data" mapstructure:"associated_data"` | 	AssociatedData string `json:"associated_data" struct:"associated_data" mapstructure:"associated_data"` | ||||||
|  |  | ||||||
|  | 	// Reference is an arbitrary caller supplied string value that will be placed on the | ||||||
|  | 	// batch response to ease correlation between inputs and outputs | ||||||
|  | 	Reference string `json:"reference" structs:"reference" mapstructure:"reference"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // EncryptBatchResponseItem represents a response item for batch processing | // EncryptBatchResponseItem represents a response item for batch processing | ||||||
| @@ -56,6 +60,10 @@ type EncryptBatchResponseItem struct { | |||||||
| 	// Error, if set represents a failure encountered while encrypting a | 	// Error, if set represents a failure encountered while encrypting a | ||||||
| 	// corresponding batch request item | 	// corresponding batch request item | ||||||
| 	Error string `json:"error,omitempty" structs:"error" mapstructure:"error"` | 	Error string `json:"error,omitempty" structs:"error" mapstructure:"error"` | ||||||
|  |  | ||||||
|  | 	// Reference is an arbitrary caller supplied string value that will be placed on the | ||||||
|  | 	// batch response to ease correlation between inputs and outputs | ||||||
|  | 	Reference string `json:"reference"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type AssocDataFactory struct { | type AssocDataFactory struct { | ||||||
| @@ -261,6 +269,14 @@ func decodeBatchRequestItems(src interface{}, requirePlaintext bool, requireCiph | |||||||
| 				errs.Errors = append(errs.Errors, fmt.Sprintf("'[%d].associated_data' expected type 'string', got unconvertible type '%T'", i, item["associated_data"])) | 				errs.Errors = append(errs.Errors, fmt.Sprintf("'[%d].associated_data' expected type 'string', got unconvertible type '%T'", i, item["associated_data"])) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		if v, has := item["reference"]; has { | ||||||
|  | 			if !reflect.ValueOf(v).IsValid() { | ||||||
|  | 			} else if casted, ok := v.(string); ok { | ||||||
|  | 				(*dst)[i].Reference = casted | ||||||
|  | 			} else { | ||||||
|  | 				errs.Errors = append(errs.Errors, fmt.Sprintf("'[%d].reference' expected type 'string', got unconvertible type '%T'", i, item["reference"])) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(errs.Errors) > 0 { | 	if len(errs.Errors) > 0 { | ||||||
| @@ -471,6 +487,10 @@ func (b *backend) pathEncryptWrite(ctx context.Context, req *logical.Request, d | |||||||
|  |  | ||||||
| 	resp := &logical.Response{} | 	resp := &logical.Response{} | ||||||
| 	if batchInputRaw != nil { | 	if batchInputRaw != nil { | ||||||
|  | 		// Copy the references | ||||||
|  | 		for i := range batchInputItems { | ||||||
|  | 			batchResponseItems[i].Reference = batchInputItems[i].Reference | ||||||
|  | 		} | ||||||
| 		resp.Data = map[string]interface{}{ | 		resp.Data = map[string]interface{}{ | ||||||
| 			"batch_results": batchResponseItems, | 			"batch_results": batchResponseItems, | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -225,7 +225,7 @@ func TestTransit_BatchEncryptionCase3(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Case4: Test batch encryption with an existing key | // Case4: Test batch encryption with an existing key (and test references) | ||||||
| func TestTransit_BatchEncryptionCase4(t *testing.T) { | func TestTransit_BatchEncryptionCase4(t *testing.T) { | ||||||
| 	var resp *logical.Response | 	var resp *logical.Response | ||||||
| 	var err error | 	var err error | ||||||
| @@ -243,8 +243,8 @@ func TestTransit_BatchEncryptionCase4(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	batchInput := []interface{}{ | 	batchInput := []interface{}{ | ||||||
| 		map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, | 		map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "reference": "b"}, | ||||||
| 		map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, | 		map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "reference": "a"}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	batchData := map[string]interface{}{ | 	batchData := map[string]interface{}{ | ||||||
| @@ -271,7 +271,7 @@ func TestTransit_BatchEncryptionCase4(t *testing.T) { | |||||||
|  |  | ||||||
| 	plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" | 	plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" | ||||||
|  |  | ||||||
| 	for _, item := range batchResponseItems { | 	for i, item := range batchResponseItems { | ||||||
| 		if item.KeyVersion != 1 { | 		if item.KeyVersion != 1 { | ||||||
| 			t.Fatalf("unexpected key version; got: %d, expected: %d", item.KeyVersion, 1) | 			t.Fatalf("unexpected key version; got: %d, expected: %d", item.KeyVersion, 1) | ||||||
| 		} | 		} | ||||||
| @@ -287,6 +287,10 @@ func TestTransit_BatchEncryptionCase4(t *testing.T) { | |||||||
| 		if resp.Data["plaintext"] != plaintext { | 		if resp.Data["plaintext"] != plaintext { | ||||||
| 			t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) | 			t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) | ||||||
| 		} | 		} | ||||||
|  | 		inputItem := batchInput[i].(map[string]interface{}) | ||||||
|  | 		if item.Reference != inputItem["reference"] { | ||||||
|  | 			t.Fatalf("reference mismatch.  Expected %s, Actual: %s", inputItem["reference"], item.Reference) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -35,6 +35,10 @@ type batchResponseHMACItem struct { | |||||||
| 	// For batch processing to successfully mimic previous handling for simple 'input', | 	// For batch processing to successfully mimic previous handling for simple 'input', | ||||||
| 	// both output values are needed - though 'err' should never be serialized. | 	// both output values are needed - though 'err' should never be serialized. | ||||||
| 	err error | 	err error | ||||||
|  |  | ||||||
|  | 	// Reference is an arbitrary caller supplied string value that will be placed on the | ||||||
|  | 	// batch response to ease correlation between inputs and outputs | ||||||
|  | 	Reference string `json:"reference" mapstructure:"reference"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (b *backend) pathHMAC() *framework.Path { | func (b *backend) pathHMAC() *framework.Path { | ||||||
| @@ -201,6 +205,10 @@ func (b *backend) pathHMACWrite(ctx context.Context, req *logical.Request, d *fr | |||||||
| 	// Generate the response | 	// Generate the response | ||||||
| 	resp := &logical.Response{} | 	resp := &logical.Response{} | ||||||
| 	if batchInputRaw != nil { | 	if batchInputRaw != nil { | ||||||
|  | 		// Copy the references | ||||||
|  | 		for i := range batchInputItems { | ||||||
|  | 			response[i].Reference = batchInputItems[i]["reference"] | ||||||
|  | 		} | ||||||
| 		resp.Data = map[string]interface{}{ | 		resp.Data = map[string]interface{}{ | ||||||
| 			"batch_results": response, | 			"batch_results": response, | ||||||
| 		} | 		} | ||||||
| @@ -362,6 +370,10 @@ func (b *backend) pathHMACVerify(ctx context.Context, req *logical.Request, d *f | |||||||
| 	// Generate the response | 	// Generate the response | ||||||
| 	resp := &logical.Response{} | 	resp := &logical.Response{} | ||||||
| 	if batchInputRaw != nil { | 	if batchInputRaw != nil { | ||||||
|  | 		// Copy the references | ||||||
|  | 		for i := range batchInputItems { | ||||||
|  | 			response[i].Reference = batchInputItems[i]["reference"] | ||||||
|  | 		} | ||||||
| 		resp.Data = map[string]interface{}{ | 		resp.Data = map[string]interface{}{ | ||||||
| 			"batch_results": response, | 			"batch_results": response, | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -248,18 +248,18 @@ func TestTransit_batchHMAC(t *testing.T) { | |||||||
|  |  | ||||||
| 	req.Path = "hmac/foo" | 	req.Path = "hmac/foo" | ||||||
| 	batchInput := []batchRequestHMACItem{ | 	batchInput := []batchRequestHMACItem{ | ||||||
| 		{"input": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, | 		{"input": "dGhlIHF1aWNrIGJyb3duIGZveA==", "reference": "one"}, | ||||||
| 		{"input": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, | 		{"input": "dGhlIHF1aWNrIGJyb3duIGZveA==", "reference": "two"}, | ||||||
| 		{"input": ""}, | 		{"input": "", "reference": "three"}, | ||||||
| 		{"input": ":;.?"}, | 		{"input": ":;.?", "reference": "four"}, | ||||||
| 		{}, | 		{}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	expected := []batchResponseHMACItem{ | 	expected := []batchResponseHMACItem{ | ||||||
| 		{HMAC: "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="}, | 		{HMAC: "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4=", Reference: "one"}, | ||||||
| 		{HMAC: "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="}, | 		{HMAC: "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4=", Reference: "two"}, | ||||||
| 		{HMAC: "vault:v1:BCfVv6rlnRsIKpjCZCxWvh5iYwSSabRXpX9XJniuNgc="}, | 		{HMAC: "vault:v1:BCfVv6rlnRsIKpjCZCxWvh5iYwSSabRXpX9XJniuNgc=", Reference: "three"}, | ||||||
| 		{Error: "unable to decode input as base64: illegal base64 data at input byte 0"}, | 		{Error: "unable to decode input as base64: illegal base64 data at input byte 0", Reference: "four"}, | ||||||
| 		{Error: "missing input for HMAC"}, | 		{Error: "missing input for HMAC"}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -286,6 +286,9 @@ func TestTransit_batchHMAC(t *testing.T) { | |||||||
| 		if expected[i].Error != "" && expected[i].Error != m.Error { | 		if expected[i].Error != "" && expected[i].Error != m.Error { | ||||||
| 			t.Fatalf("Expected Error %q got %q in result %d", expected[i].Error, m.Error, i) | 			t.Fatalf("Expected Error %q got %q in result %d", expected[i].Error, m.Error, i) | ||||||
| 		} | 		} | ||||||
|  | 		if expected[i].Reference != m.Reference { | ||||||
|  | 			t.Fatalf("Expected references to match, Got %s, Expected %s", m.Reference, expected[i].Reference) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Verify a previous version | 	// Verify a previous version | ||||||
|   | |||||||
| @@ -182,6 +182,10 @@ func (b *backend) pathRewrapWrite(ctx context.Context, req *logical.Request, d * | |||||||
|  |  | ||||||
| 	resp := &logical.Response{} | 	resp := &logical.Response{} | ||||||
| 	if batchInputRaw != nil { | 	if batchInputRaw != nil { | ||||||
|  | 		// Copy the references | ||||||
|  | 		for i := range batchInputItems { | ||||||
|  | 			batchResponseItems[i].Reference = batchInputItems[i].Reference | ||||||
|  | 		} | ||||||
| 		resp.Data = map[string]interface{}{ | 		resp.Data = map[string]interface{}{ | ||||||
| 			"batch_results": batchResponseItems, | 			"batch_results": batchResponseItems, | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -224,8 +224,8 @@ func TestTransit_BatchRewrapCase3(t *testing.T) { | |||||||
| 	b, s := createBackendWithStorage(t) | 	b, s := createBackendWithStorage(t) | ||||||
|  |  | ||||||
| 	batchEncryptionInput := []interface{}{ | 	batchEncryptionInput := []interface{}{ | ||||||
| 		map[string]interface{}{"plaintext": "dmlzaGFsCg=="}, | 		map[string]interface{}{"plaintext": "dmlzaGFsCg==", "reference": "ek"}, | ||||||
| 		map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, | 		map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "reference": "do"}, | ||||||
| 	} | 	} | ||||||
| 	batchEncryptionData := map[string]interface{}{ | 	batchEncryptionData := map[string]interface{}{ | ||||||
| 		"batch_input": batchEncryptionInput, | 		"batch_input": batchEncryptionInput, | ||||||
| @@ -245,7 +245,7 @@ func TestTransit_BatchRewrapCase3(t *testing.T) { | |||||||
|  |  | ||||||
| 	batchRewrapInput := make([]interface{}, len(batchEncryptionResponseItems)) | 	batchRewrapInput := make([]interface{}, len(batchEncryptionResponseItems)) | ||||||
| 	for i, item := range batchEncryptionResponseItems { | 	for i, item := range batchEncryptionResponseItems { | ||||||
| 		batchRewrapInput[i] = map[string]interface{}{"ciphertext": item.Ciphertext} | 		batchRewrapInput[i] = map[string]interface{}{"ciphertext": item.Ciphertext, "reference": item.Reference} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	batchRewrapData := map[string]interface{}{ | 	batchRewrapData := map[string]interface{}{ | ||||||
| @@ -289,6 +289,11 @@ func TestTransit_BatchRewrapCase3(t *testing.T) { | |||||||
| 	for i, eItem := range batchEncryptionResponseItems { | 	for i, eItem := range batchEncryptionResponseItems { | ||||||
| 		rItem := batchRewrapResponseItems[i] | 		rItem := batchRewrapResponseItems[i] | ||||||
|  |  | ||||||
|  | 		inputRef := batchEncryptionInput[i].(map[string]interface{})["reference"] | ||||||
|  | 		if eItem.Reference != inputRef { | ||||||
|  | 			t.Fatalf("bad: reference mismatch. Expected %s, Actual: %s", inputRef, eItem.Reference) | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if eItem.Ciphertext == rItem.Ciphertext { | 		if eItem.Ciphertext == rItem.Ciphertext { | ||||||
| 			t.Fatalf("bad: rewrap input and output are the same") | 			t.Fatalf("bad: rewrap input and output are the same") | ||||||
| 		} | 		} | ||||||
| @@ -315,5 +320,6 @@ func TestTransit_BatchRewrapCase3(t *testing.T) { | |||||||
| 		if resp.Data["plaintext"] != plaintext1 && resp.Data["plaintext"] != plaintext2 { | 		if resp.Data["plaintext"] != plaintext1 && resp.Data["plaintext"] != plaintext2 { | ||||||
| 			t.Fatalf("bad: plaintext. Expected: %q or %q, Actual: %q", plaintext1, plaintext2, resp.Data["plaintext"]) | 			t.Fatalf("bad: plaintext. Expected: %q or %q, Actual: %q", plaintext1, plaintext2, resp.Data["plaintext"]) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -39,6 +39,10 @@ type batchResponseSignItem struct { | |||||||
| 	// For batch processing to successfully mimic previous handling for simple 'input', | 	// For batch processing to successfully mimic previous handling for simple 'input', | ||||||
| 	// both output values are needed - though 'err' should never be serialized. | 	// both output values are needed - though 'err' should never be serialized. | ||||||
| 	err error | 	err error | ||||||
|  |  | ||||||
|  | 	// Reference is an arbitrary caller supplied string value that will be placed on the | ||||||
|  | 	// batch response to ease correlation between inputs and outputs | ||||||
|  | 	Reference string `json:"reference" mapstructure:"reference"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // BatchRequestVerifyItem represents a request item for batch processing. | // BatchRequestVerifyItem represents a request item for batch processing. | ||||||
| @@ -59,6 +63,10 @@ type batchResponseVerifyItem struct { | |||||||
| 	// For batch processing to successfully mimic previous handling for simple 'input', | 	// For batch processing to successfully mimic previous handling for simple 'input', | ||||||
| 	// both output values are needed - though 'err' should never be serialized. | 	// both output values are needed - though 'err' should never be serialized. | ||||||
| 	err error | 	err error | ||||||
|  |  | ||||||
|  | 	// Reference is an arbitrary caller supplied string value that will be placed on the | ||||||
|  | 	// batch response to ease correlation between inputs and outputs | ||||||
|  | 	Reference string `json:"reference" mapstructure:"reference"` | ||||||
| } | } | ||||||
|  |  | ||||||
| const defaultHashAlgorithm = "sha2-256" | const defaultHashAlgorithm = "sha2-256" | ||||||
| @@ -420,6 +428,10 @@ func (b *backend) pathSignWrite(ctx context.Context, req *logical.Request, d *fr | |||||||
| 	// Generate the response | 	// Generate the response | ||||||
| 	resp := &logical.Response{} | 	resp := &logical.Response{} | ||||||
| 	if batchInputRaw != nil { | 	if batchInputRaw != nil { | ||||||
|  | 		// Copy the references | ||||||
|  | 		for i := range batchInputItems { | ||||||
|  | 			response[i].Reference = batchInputItems[i]["reference"] | ||||||
|  | 		} | ||||||
| 		resp.Data = map[string]interface{}{ | 		resp.Data = map[string]interface{}{ | ||||||
| 			"batch_results": response, | 			"batch_results": response, | ||||||
| 		} | 		} | ||||||
| @@ -636,6 +648,10 @@ func (b *backend) pathVerifyWrite(ctx context.Context, req *logical.Request, d * | |||||||
| 	// Generate the response | 	// Generate the response | ||||||
| 	resp := &logical.Response{} | 	resp := &logical.Response{} | ||||||
| 	if batchInputRaw != nil { | 	if batchInputRaw != nil { | ||||||
|  | 		// Copy the references | ||||||
|  | 		for i := range batchInputItems { | ||||||
|  | 			response[i].Reference = batchInputItems[i]["reference"] | ||||||
|  | 		} | ||||||
| 		resp.Data = map[string]interface{}{ | 		resp.Data = map[string]interface{}{ | ||||||
| 			"batch_results": response, | 			"batch_results": response, | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ type signOutcome struct { | |||||||
| 	requestOk bool | 	requestOk bool | ||||||
| 	valid     bool | 	valid     bool | ||||||
| 	keyValid  bool | 	keyValid  bool | ||||||
|  | 	reference string | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestTransit_SignVerify_ECDSA(t *testing.T) { | func TestTransit_SignVerify_ECDSA(t *testing.T) { | ||||||
| @@ -483,6 +484,7 @@ func TestTransit_SignVerify_ED25519(t *testing.T) { | |||||||
| 			} | 			} | ||||||
| 			for i, v := range sig { | 			for i, v := range sig { | ||||||
| 				batchRequestItems[i]["signature"] = v | 				batchRequestItems[i]["signature"] = v | ||||||
|  | 				batchRequestItems[i]["reference"] = outcome[i].reference | ||||||
| 			} | 			} | ||||||
| 		} else if attachSig { | 		} else if attachSig { | ||||||
| 			req.Data["signature"] = sig[0] | 			req.Data["signature"] = sig[0] | ||||||
| @@ -535,6 +537,9 @@ func TestTransit_SignVerify_ED25519(t *testing.T) { | |||||||
| 				if pubKeyRaw, ok := req.Data["public_key"]; ok { | 				if pubKeyRaw, ok := req.Data["public_key"]; ok { | ||||||
| 					validatePublicKey(t, batchRequestItems[i]["input"], sig[i], pubKeyRaw.([]byte), outcome[i].keyValid, postpath, b) | 					validatePublicKey(t, batchRequestItems[i]["input"], sig[i], pubKeyRaw.([]byte), outcome[i].keyValid, postpath, b) | ||||||
| 				} | 				} | ||||||
|  | 				if v.Reference != outcome[i].reference { | ||||||
|  | 					t.Fatalf("verification failed, mismatched references %s vs %s", v.Reference, outcome[i].reference) | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -634,15 +639,18 @@ func TestTransit_SignVerify_ED25519(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Test Batch Signing | 	// Test Batch Signing | ||||||
| 	batchInput := []batchRequestSignItem{ | 	batchInput := []batchRequestSignItem{ | ||||||
| 		{"context": "abcd", "input": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, | 		{"context": "abcd", "input": "dGhlIHF1aWNrIGJyb3duIGZveA==", "reference": "uno"}, | ||||||
| 		{"context": "efgh", "input": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, | 		{"context": "efgh", "input": "dGhlIHF1aWNrIGJyb3duIGZveA==", "reference": "dos"}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	req.Data = map[string]interface{}{ | 	req.Data = map[string]interface{}{ | ||||||
| 		"batch_input": batchInput, | 		"batch_input": batchInput, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	outcome = []signOutcome{{requestOk: true, valid: true, keyValid: true}, {requestOk: true, valid: true, keyValid: true}} | 	outcome = []signOutcome{ | ||||||
|  | 		{requestOk: true, valid: true, keyValid: true, reference: "uno"}, | ||||||
|  | 		{requestOk: true, valid: true, keyValid: true, reference: "dos"}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	sig = signRequest(req, false, "foo") | 	sig = signRequest(req, false, "foo") | ||||||
| 	verifyRequest(req, false, outcome, "foo", sig, true) | 	verifyRequest(req, false, outcome, "foo", sig, true) | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								changelog/18243.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								changelog/18243.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | ```release-note:improvement | ||||||
|  | secrets/transit: Add an optional reference field to batch operation items | ||||||
|  | which is repeated on batch responses to help more easily correlate inputs with outputs. | ||||||
|  | ``` | ||||||
| @@ -627,6 +627,12 @@ will be returned. | |||||||
|   for any given context (and thus, any given encryption key) this nonce value is |   for any given context (and thus, any given encryption key) this nonce value is | ||||||
|   **never reused**. |   **never reused**. | ||||||
|  |  | ||||||
|  | - `reference` `(string: "")` - | ||||||
|  |   A user-supplied string that will be present in the `reference` field on the | ||||||
|  |   corresponding `batch_results` item in the response, to assist in understanding | ||||||
|  |   which result corresponds to a particular input. Only valid on batch requests | ||||||
|  |   when using ‘batch_input’ below. | ||||||
|  |  | ||||||
| - `batch_input` `(array<object>: nil)` – Specifies a list of items to be | - `batch_input` `(array<object>: nil)` – Specifies a list of items to be | ||||||
|   encrypted in a single batch. When this parameter is set, if the parameters |   encrypted in a single batch. When this parameter is set, if the parameters | ||||||
|   'plaintext', 'context' and 'nonce' are also set, they will be ignored.  |   'plaintext', 'context' and 'nonce' are also set, they will be ignored.  | ||||||
| @@ -740,6 +746,12 @@ This endpoint decrypts the provided ciphertext using the named key. | |||||||
|   and the key was generated with Vault 0.6.1. Not required for keys created in |   and the key was generated with Vault 0.6.1. Not required for keys created in | ||||||
|   0.6.2+. |   0.6.2+. | ||||||
|  |  | ||||||
|  | - `reference` `(string: "")` - | ||||||
|  |   A user-supplied string that will be present in the `reference` field on the | ||||||
|  |   corresponding `batch_results` item in the response, to assist in understanding | ||||||
|  |   which result corresponds to a particular input. Only valid on batch requests | ||||||
|  |   when using ‘batch_input’ below. | ||||||
|  |  | ||||||
| - `batch_input` `(array<object>: nil)` – Specifies a list of items to be | - `batch_input` `(array<object>: nil)` – Specifies a list of items to be | ||||||
|   decrypted in a single batch. When this parameter is set, if the parameters |   decrypted in a single batch. When this parameter is set, if the parameters | ||||||
|   'ciphertext', 'context' and 'nonce' are also set, they will be ignored.  |   'ciphertext', 'context' and 'nonce' are also set, they will be ignored.  | ||||||
| @@ -824,6 +836,12 @@ functionality to untrusted users or scripts. | |||||||
|   and the key was generated with Vault 0.6.1. Not required for keys created in |   and the key was generated with Vault 0.6.1. Not required for keys created in | ||||||
|   0.6.2+. |   0.6.2+. | ||||||
|  |  | ||||||
|  | - `reference` `(string: "")` - | ||||||
|  |   A user-supplied string that will be present in the `reference` field on the | ||||||
|  |   corresponding `batch_results` item in the response, to assist in understanding | ||||||
|  |   which result corresponds to a particular input. Only valid on batch requests | ||||||
|  |   when using ‘batch_input’ below. | ||||||
|  |  | ||||||
| - `batch_input` `(array<object>: nil)` – Specifies a list of items to be | - `batch_input` `(array<object>: nil)` – Specifies a list of items to be | ||||||
|   decrypted in a single batch. When this parameter is set, if the parameters |   decrypted in a single batch. When this parameter is set, if the parameters | ||||||
|   'ciphertext', 'context' and 'nonce' are also set, they will be ignored.  |   'ciphertext', 'context' and 'nonce' are also set, they will be ignored.  | ||||||
| @@ -1085,6 +1103,12 @@ be used. | |||||||
| - `input` `(string: "")` – Specifies the **base64 encoded** input data. One of | - `input` `(string: "")` – Specifies the **base64 encoded** input data. One of | ||||||
|   `input` or `batch_input` must be supplied. |   `input` or `batch_input` must be supplied. | ||||||
|  |  | ||||||
|  | - `reference` `(string: "")` - | ||||||
|  |   A user-supplied string that will be present in the `reference` field on the | ||||||
|  |   corresponding `batch_results` item in the response, to assist in understanding | ||||||
|  |   which result corresponds to a particular input. Only valid on batch requests | ||||||
|  |   when using ‘batch_input’ below. | ||||||
|  |  | ||||||
| - `batch_input` `(array<object>: nil)` – Specifies a list of items for processing. | - `batch_input` `(array<object>: nil)` – Specifies a list of items for processing. | ||||||
|   When this parameter is set, if the parameter 'input' is also set, it will be |   When this parameter is set, if the parameter 'input' is also set, it will be | ||||||
|   ignored. Responses are returned in the 'batch_results' array component of the |   ignored. Responses are returned in the 'batch_results' array component of the | ||||||
| @@ -1233,6 +1257,12 @@ supports signing. | |||||||
| - `input` `(string: "")` – Specifies the **base64 encoded** input data. One of | - `input` `(string: "")` – Specifies the **base64 encoded** input data. One of | ||||||
|   `input` or `batch_input` must be supplied. |   `input` or `batch_input` must be supplied. | ||||||
|  |  | ||||||
|  | - `reference` `(string: "")` - | ||||||
|  |   A user-supplied string that will be present in the `reference` field on the | ||||||
|  |   corresponding `batch_results` item in the response, to assist in understanding | ||||||
|  |   which result corresponds to a particular input. Only valid on batch requests | ||||||
|  |   when using ‘batch_input’ below. | ||||||
|  |  | ||||||
| - `batch_input` `(array<object>: nil)` – Specifies a list of items for processing. | - `batch_input` `(array<object>: nil)` – Specifies a list of items for processing. | ||||||
|   When this parameter is set, any supplied 'input' or 'context' parameters will be |   When this parameter is set, any supplied 'input' or 'context' parameters will be | ||||||
|   ignored. Responses are returned in the 'batch_results' array component of the |   ignored. Responses are returned in the 'batch_results' array component of the | ||||||
| @@ -1417,6 +1447,12 @@ data. | |||||||
|   `/transit/hmac` function. Either this must be supplied or `signature` must be |   `/transit/hmac` function. Either this must be supplied or `signature` must be | ||||||
|   supplied. |   supplied. | ||||||
|  |  | ||||||
|  | - `reference` `(string: "")` - | ||||||
|  |   A user-supplied string that will be present in the `reference` field on the | ||||||
|  |   corresponding `batch_results` item in the response, to assist in understanding | ||||||
|  |   which result corresponds to a particular input. Only valid on batch requests | ||||||
|  |   when using ‘batch_input’ below. | ||||||
|  |  | ||||||
| - `batch_input` `(array<object>: nil)` – Specifies a list of items for processing. | - `batch_input` `(array<object>: nil)` – Specifies a list of items for processing. | ||||||
|   When this parameter is set, any supplied 'input', 'hmac' or 'signature' parameters |   When this parameter is set, any supplied 'input', 'hmac' or 'signature' parameters | ||||||
|   will be ignored. 'batch_input' items should contain an 'input' parameter and |   will be ignored. 'batch_input' items should contain an 'input' parameter and | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Scott Miller
					Scott Miller