mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 02:02:43 +00:00 
			
		
		
		
	 d6a1ce2e7b
			
		
	
	d6a1ce2e7b
	
	
	
		
			
			* Get import correct * limits, docs * changelog * unit tests * And fix import for hmac unit test * typo * Update website/content/api-docs/secret/transit.mdx Co-authored-by: Matt Schultz <975680+schultz-is@users.noreply.github.com> * Update builtin/logical/transit/path_keys.go Co-authored-by: Matt Schultz <975680+schultz-is@users.noreply.github.com> * Validate key sizes a bit more carefully * Update sdk/helper/keysutil/policy.go Co-authored-by: Matt Schultz <975680+schultz-is@users.noreply.github.com> Co-authored-by: Matt Schultz <975680+schultz-is@users.noreply.github.com>
		
			
				
	
	
		
			369 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package transit
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/hashicorp/vault/sdk/helper/keysutil"
 | |
| 	"github.com/hashicorp/vault/sdk/logical"
 | |
| )
 | |
| 
 | |
| func TestTransit_HMAC(t *testing.T) {
 | |
| 	b, storage := createBackendWithSysView(t)
 | |
| 
 | |
| 	cases := []struct {
 | |
| 		name string
 | |
| 		typ  string
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "foo",
 | |
| 			typ:  "",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "dedicated",
 | |
| 			typ:  "hmac",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, c := range cases {
 | |
| 		req := &logical.Request{
 | |
| 			Storage:   storage,
 | |
| 			Operation: logical.UpdateOperation,
 | |
| 			Path:      "keys/" + c.name,
 | |
| 		}
 | |
| 		_, err := b.HandleRequest(context.Background(), req)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		// Now, change the key value to something we control
 | |
| 		p, _, err := b.GetPolicy(context.Background(), keysutil.PolicyRequest{
 | |
| 			Storage: storage,
 | |
| 			Name:    c.name,
 | |
| 		}, b.GetRandomReader())
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		// We don't care as we're the only one using this
 | |
| 		latestVersion := strconv.Itoa(p.LatestVersion)
 | |
| 		keyEntry := p.Keys[latestVersion]
 | |
| 		keyEntry.HMACKey = []byte("01234567890123456789012345678901")
 | |
| 		keyEntry.Key = []byte("01234567890123456789012345678901")
 | |
| 		p.Keys[latestVersion] = keyEntry
 | |
| 		if err = p.Persist(context.Background(), storage); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		req.Path = "hmac/" + c.name
 | |
| 		req.Data = map[string]interface{}{
 | |
| 			"input": "dGhlIHF1aWNrIGJyb3duIGZveA==",
 | |
| 		}
 | |
| 
 | |
| 		doRequest := func(req *logical.Request, errExpected bool, expected string) {
 | |
| 			path := req.Path
 | |
| 			defer func() { req.Path = path }()
 | |
| 
 | |
| 			resp, err := b.HandleRequest(context.Background(), req)
 | |
| 			if err != nil && !errExpected {
 | |
| 				panic(fmt.Sprintf("%v", err))
 | |
| 			}
 | |
| 			if resp == nil {
 | |
| 				t.Fatal("expected non-nil response")
 | |
| 			}
 | |
| 			if errExpected {
 | |
| 				if !resp.IsError() {
 | |
| 					t.Fatalf("bad: got error response: %#v", *resp)
 | |
| 				}
 | |
| 				return
 | |
| 			}
 | |
| 			if resp.IsError() {
 | |
| 				t.Fatalf("bad: got error response: %#v", *resp)
 | |
| 			}
 | |
| 			value, ok := resp.Data["hmac"]
 | |
| 			if !ok {
 | |
| 				t.Fatalf("no hmac key found in returned data, got resp data %#v", resp.Data)
 | |
| 			}
 | |
| 			if value.(string) != expected {
 | |
| 				panic(fmt.Sprintf("mismatched hashes; expected %s, got resp data %#v", expected, resp.Data))
 | |
| 			}
 | |
| 
 | |
| 			// Now verify
 | |
| 			req.Path = strings.ReplaceAll(req.Path, "hmac", "verify")
 | |
| 			req.Data["hmac"] = value.(string)
 | |
| 			resp, err = b.HandleRequest(context.Background(), req)
 | |
| 			if err != nil {
 | |
| 				t.Fatalf("%v: %v", err, resp)
 | |
| 			}
 | |
| 			if resp == nil {
 | |
| 				t.Fatal("expected non-nil response")
 | |
| 			}
 | |
| 			if resp.Data["valid"].(bool) == false {
 | |
| 				panic(fmt.Sprintf("error validating hmac;\nreq:\n%#v\nresp:\n%#v", *req, *resp))
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Comparisons are against values generated via openssl
 | |
| 
 | |
| 		// Test defaults -- sha2-256
 | |
| 		doRequest(req, false, "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4=")
 | |
| 
 | |
| 		// Test algorithm selection in the path
 | |
| 		req.Path = "hmac/" + c.name + "/sha2-224"
 | |
| 		doRequest(req, false, "vault:v1:3p+ZWVquYDvu2dSTCa65Y3fgoMfIAc6fNaBbtg==")
 | |
| 
 | |
| 		// Reset and test algorithm selection in the data
 | |
| 		req.Path = "hmac/" + c.name
 | |
| 		req.Data["algorithm"] = "sha2-224"
 | |
| 		doRequest(req, false, "vault:v1:3p+ZWVquYDvu2dSTCa65Y3fgoMfIAc6fNaBbtg==")
 | |
| 
 | |
| 		req.Data["algorithm"] = "sha2-384"
 | |
| 		doRequest(req, false, "vault:v1:jDB9YXdPjpmr29b1JCIEJO93IydlKVfD9mA2EO9OmJtJQg3QAV5tcRRRb7IQGW9p")
 | |
| 
 | |
| 		req.Data["algorithm"] = "sha2-512"
 | |
| 		doRequest(req, false, "vault:v1:PSXLXvkvKF4CpU65e2bK1tGBZQpcpCEM32fq2iUoiTyQQCfBcGJJItQ+60tMwWXAPQrC290AzTrNJucGrr4GFA==")
 | |
| 
 | |
| 		// Test returning as base64
 | |
| 		req.Data["format"] = "base64"
 | |
| 		doRequest(req, false, "vault:v1:PSXLXvkvKF4CpU65e2bK1tGBZQpcpCEM32fq2iUoiTyQQCfBcGJJItQ+60tMwWXAPQrC290AzTrNJucGrr4GFA==")
 | |
| 
 | |
| 		// Test SHA3
 | |
| 		req.Path = "hmac/" + c.name
 | |
| 		req.Data["algorithm"] = "sha3-224"
 | |
| 		doRequest(req, false, "vault:v1:TGipmKH8LR/BkMolYpDYy0BJCIhTtGPDhV2VkQ==")
 | |
| 
 | |
| 		req.Data["algorithm"] = "sha3-256"
 | |
| 		doRequest(req, false, "vault:v1:+px9V/7QYLfdK808zPESC2T/L33uFf4Blzsn9Jy838o=")
 | |
| 
 | |
| 		req.Data["algorithm"] = "sha3-384"
 | |
| 		doRequest(req, false, "vault:v1:YGoRwN4UdTRYZeOER86jsQOB8piWenzLDzJ2wJQK/Jq59rAsY8lh7SCdqqCyFg70")
 | |
| 
 | |
| 		req.Data["algorithm"] = "sha3-512"
 | |
| 		doRequest(req, false, "vault:v1:GrNA8sU88naMPEQ7UZGj9EJl7YJhl03AFHfxcEURFrtvnobdea9ZlZHePpxAx/oCaC7R2HkrAO+Tu3uXPIl3lg==")
 | |
| 
 | |
| 		// Test returning SHA3 as base64
 | |
| 		req.Data["format"] = "base64"
 | |
| 		doRequest(req, false, "vault:v1:GrNA8sU88naMPEQ7UZGj9EJl7YJhl03AFHfxcEURFrtvnobdea9ZlZHePpxAx/oCaC7R2HkrAO+Tu3uXPIl3lg==")
 | |
| 
 | |
| 		req.Data["algorithm"] = "foobar"
 | |
| 		doRequest(req, true, "")
 | |
| 
 | |
| 		req.Data["algorithm"] = "sha2-256"
 | |
| 		req.Data["input"] = "foobar"
 | |
| 		doRequest(req, true, "")
 | |
| 		req.Data["input"] = "dGhlIHF1aWNrIGJyb3duIGZveA=="
 | |
| 
 | |
| 		// Rotate
 | |
| 		err = p.Rotate(context.Background(), storage, b.GetRandomReader())
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		keyEntry = p.Keys["2"]
 | |
| 		// Set to another value we control
 | |
| 		keyEntry.HMACKey = []byte("12345678901234567890123456789012")
 | |
| 		p.Keys["2"] = keyEntry
 | |
| 		if err = p.Persist(context.Background(), storage); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		doRequest(req, false, "vault:v2:Dt+mO/B93kuWUbGMMobwUNX5Wodr6dL3JH4DMfpQ0kw=")
 | |
| 
 | |
| 		// Verify a previous version
 | |
| 		req.Path = "verify/" + c.name
 | |
| 
 | |
| 		req.Data["hmac"] = "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
 | |
| 		resp, err := b.HandleRequest(context.Background(), req)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("%v: %v", err, resp)
 | |
| 		}
 | |
| 		if resp == nil {
 | |
| 			t.Fatal("expected non-nil response")
 | |
| 		}
 | |
| 		if resp.Data["valid"].(bool) == false {
 | |
| 			t.Fatalf("error validating hmac\nreq\n%#v\nresp\n%#v", *req, *resp)
 | |
| 		}
 | |
| 
 | |
| 		// Try a bad value
 | |
| 		req.Data["hmac"] = "vault:v1:UcBvm4VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
 | |
| 		resp, err = b.HandleRequest(context.Background(), req)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("%v: %v", err, resp)
 | |
| 		}
 | |
| 		if resp == nil {
 | |
| 			t.Fatal("expected non-nil response")
 | |
| 		}
 | |
| 		if resp.Data["valid"].(bool) {
 | |
| 			t.Fatalf("expected error validating hmac")
 | |
| 		}
 | |
| 
 | |
| 		// Set min decryption version, attempt to verify
 | |
| 		p.MinDecryptionVersion = 2
 | |
| 		if err = p.Persist(context.Background(), storage); err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		req.Data["hmac"] = "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
 | |
| 		resp, err = b.HandleRequest(context.Background(), req)
 | |
| 		if err == nil {
 | |
| 			t.Fatalf("expected an error, got response %#v", resp)
 | |
| 		}
 | |
| 		if err != logical.ErrInvalidRequest {
 | |
| 			t.Fatalf("expected invalid request error, got %v", err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestTransit_batchHMAC(t *testing.T) {
 | |
| 	b, storage := createBackendWithSysView(t)
 | |
| 
 | |
| 	// First create a key
 | |
| 	req := &logical.Request{
 | |
| 		Storage:   storage,
 | |
| 		Operation: logical.UpdateOperation,
 | |
| 		Path:      "keys/foo",
 | |
| 	}
 | |
| 	_, err := b.HandleRequest(context.Background(), req)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	// Now, change the key value to something we control
 | |
| 	p, _, err := b.GetPolicy(context.Background(), keysutil.PolicyRequest{
 | |
| 		Storage: storage,
 | |
| 		Name:    "foo",
 | |
| 	}, b.GetRandomReader())
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	// We don't care as we're the only one using this
 | |
| 	latestVersion := strconv.Itoa(p.LatestVersion)
 | |
| 	keyEntry := p.Keys[latestVersion]
 | |
| 	keyEntry.HMACKey = []byte("01234567890123456789012345678901")
 | |
| 	p.Keys[latestVersion] = keyEntry
 | |
| 	if err = p.Persist(context.Background(), storage); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	req.Path = "hmac/foo"
 | |
| 	batchInput := []batchRequestHMACItem{
 | |
| 		{"input": "dGhlIHF1aWNrIGJyb3duIGZveA=="},
 | |
| 		{"input": "dGhlIHF1aWNrIGJyb3duIGZveA=="},
 | |
| 		{"input": ""},
 | |
| 		{"input": ":;.?"},
 | |
| 		{},
 | |
| 	}
 | |
| 
 | |
| 	expected := []batchResponseHMACItem{
 | |
| 		{HMAC: "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="},
 | |
| 		{HMAC: "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="},
 | |
| 		{HMAC: "vault:v1:BCfVv6rlnRsIKpjCZCxWvh5iYwSSabRXpX9XJniuNgc="},
 | |
| 		{Error: "unable to decode input as base64: illegal base64 data at input byte 0"},
 | |
| 		{Error: "missing input for HMAC"},
 | |
| 	}
 | |
| 
 | |
| 	req.Data = map[string]interface{}{
 | |
| 		"batch_input": batchInput,
 | |
| 	}
 | |
| 
 | |
| 	resp, err := b.HandleRequest(context.Background(), req)
 | |
| 
 | |
| 	if err != nil || (resp != nil && resp.IsError()) {
 | |
| 		t.Fatalf("err:%v resp:%#v", err, resp)
 | |
| 	}
 | |
| 
 | |
| 	batchResponseItems := resp.Data["batch_results"].([]batchResponseHMACItem)
 | |
| 
 | |
| 	if len(batchResponseItems) != len(batchInput) {
 | |
| 		t.Fatalf("Expected %d items in response. Got %d", len(batchInput), len(batchResponseItems))
 | |
| 	}
 | |
| 
 | |
| 	for i, m := range batchResponseItems {
 | |
| 		if expected[i].Error == "" && expected[i].HMAC != m.HMAC {
 | |
| 			t.Fatalf("Expected HMAC %s got %s in result %d", expected[i].HMAC, m.HMAC, i)
 | |
| 		}
 | |
| 		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)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Verify a previous version
 | |
| 	req.Path = "verify/foo"
 | |
| 	good_hmac := "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
 | |
| 	bad_hmac := "vault:v1:UcBvm4VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
 | |
| 	verifyBatch := []batchRequestHMACItem{
 | |
| 		{"input": "dGhlIHF1aWNrIGJyb3duIGZveA==", "hmac": good_hmac},
 | |
| 	}
 | |
| 
 | |
| 	req.Data = map[string]interface{}{
 | |
| 		"batch_input": verifyBatch,
 | |
| 	}
 | |
| 
 | |
| 	resp, err = b.HandleRequest(context.Background(), req)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("%v: %v", err, resp)
 | |
| 	}
 | |
| 	if resp == nil {
 | |
| 		t.Fatal("expected non-nil response")
 | |
| 	}
 | |
| 
 | |
| 	batchHMACVerifyResponseItems := resp.Data["batch_results"].([]batchResponseHMACItem)
 | |
| 
 | |
| 	if !batchHMACVerifyResponseItems[0].Valid {
 | |
| 		t.Fatalf("error validating hmac\nreq\n%#v\nresp\n%#v", *req, *resp)
 | |
| 	}
 | |
| 
 | |
| 	// Try a bad value
 | |
| 	verifyBatch[0]["hmac"] = bad_hmac
 | |
| 	resp, err = b.HandleRequest(context.Background(), req)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("%v: %v", err, resp)
 | |
| 	}
 | |
| 	if resp == nil {
 | |
| 		t.Fatal("expected non-nil response")
 | |
| 	}
 | |
| 
 | |
| 	batchHMACVerifyResponseItems = resp.Data["batch_results"].([]batchResponseHMACItem)
 | |
| 
 | |
| 	if batchHMACVerifyResponseItems[0].Valid {
 | |
| 		t.Fatalf("expected error validating hmac\nreq\n%#v\nresp\n%#v", *req, *resp)
 | |
| 	}
 | |
| 
 | |
| 	// Rotate
 | |
| 	err = p.Rotate(context.Background(), storage, b.GetRandomReader())
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	keyEntry = p.Keys["2"]
 | |
| 	// Set to another value we control
 | |
| 	keyEntry.HMACKey = []byte("12345678901234567890123456789012")
 | |
| 	p.Keys["2"] = keyEntry
 | |
| 	if err = p.Persist(context.Background(), storage); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	// Set min decryption version, attempt to verify
 | |
| 	p.MinDecryptionVersion = 2
 | |
| 	if err = p.Persist(context.Background(), storage); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	// supply a good hmac, but with expired key version
 | |
| 	verifyBatch[0]["hmac"] = good_hmac
 | |
| 
 | |
| 	resp, err = b.HandleRequest(context.Background(), req)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("%v: %v", err, resp)
 | |
| 	}
 | |
| 	if resp == nil {
 | |
| 		t.Fatal("expected non-nil response")
 | |
| 	}
 | |
| 
 | |
| 	batchHMACVerifyResponseItems = resp.Data["batch_results"].([]batchResponseHMACItem)
 | |
| 
 | |
| 	if batchHMACVerifyResponseItems[0].Valid {
 | |
| 		t.Fatalf("expected error validating hmac\nreq\n%#v\nresp\n%#v", *req, *resp)
 | |
| 	}
 | |
| }
 |