mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00:00 
			
		
		
		
	Provide base64 keys in addition to hex encoded. (#1734)
* Provide base64 keys in addition to hex encoded. Accept these at unseal/rekey time. Also fix a bug where backup would not be honored when doing a rekey with no operation currently ongoing.
This commit is contained in:
		| @@ -45,7 +45,9 @@ type InitStatusResponse struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type InitResponse struct { | type InitResponse struct { | ||||||
| 	Keys         []string `json:"keys"` | 	Keys            []string `json:"keys"` | ||||||
| 	RecoveryKeys []string `json:"recovery_keys"` | 	KeysB64         []string `json:"keys_base64"` | ||||||
| 	RootToken    string   `json:"root_token"` | 	RecoveryKeys    []string `json:"recovery_keys"` | ||||||
|  | 	RecoveryKeysB64 []string `json:"recovery_keys_base64"` | ||||||
|  | 	RootToken       string   `json:"root_token"` | ||||||
| } | } | ||||||
|   | |||||||
| @@ -190,11 +190,13 @@ type RekeyUpdateResponse struct { | |||||||
| 	Nonce           string | 	Nonce           string | ||||||
| 	Complete        bool | 	Complete        bool | ||||||
| 	Keys            []string | 	Keys            []string | ||||||
|  | 	KeysB64         []string `json:"keys_base64"` | ||||||
| 	PGPFingerprints []string `json:"pgp_fingerprints"` | 	PGPFingerprints []string `json:"pgp_fingerprints"` | ||||||
| 	Backup          bool | 	Backup          bool | ||||||
| } | } | ||||||
|  |  | ||||||
| type RekeyRetrieveResponse struct { | type RekeyRetrieveResponse struct { | ||||||
| 	Nonce string | 	Nonce   string | ||||||
| 	Keys  map[string][]string | 	Keys    map[string][]string | ||||||
|  | 	KeysB64 map[string][]string `json:"keys_base64"` | ||||||
| } | } | ||||||
|   | |||||||
| @@ -192,10 +192,20 @@ func (c *InitCommand) runInit(check bool, initRequest *api.InitRequest) int { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for i, key := range resp.Keys { | 	for i, key := range resp.Keys { | ||||||
| 		c.Ui.Output(fmt.Sprintf("Unseal Key %d: %s", i+1, key)) | 		if resp.KeysB64 != nil && len(resp.KeysB64) == len(resp.Keys) { | ||||||
|  | 			c.Ui.Output(fmt.Sprintf("Unseal Key %d (hex)   : %s", i+1, key)) | ||||||
|  | 			c.Ui.Output(fmt.Sprintf("Unseal Key %d (base64): %s", i+1, resp.KeysB64[i])) | ||||||
|  | 		} else { | ||||||
|  | 			c.Ui.Output(fmt.Sprintf("Unseal Key %d: %s", i+1, key)) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	for i, key := range resp.RecoveryKeys { | 	for i, key := range resp.RecoveryKeys { | ||||||
| 		c.Ui.Output(fmt.Sprintf("Recovery Key %d: %s", i+1, key)) | 		if resp.RecoveryKeysB64 != nil && len(resp.RecoveryKeysB64) == len(resp.RecoveryKeys) { | ||||||
|  | 			c.Ui.Output(fmt.Sprintf("Recovery Key %d (hex)   : %s", i+1, key)) | ||||||
|  | 			c.Ui.Output(fmt.Sprintf("Recovery Key %d (base64): %s", i+1, resp.RecoveryKeysB64[i])) | ||||||
|  | 		} else { | ||||||
|  | 			c.Ui.Output(fmt.Sprintf("Recovery Key %d: %s", i+1, key)) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	c.Ui.Output(fmt.Sprintf("Initial Root Token: %s", resp.RootToken)) | 	c.Ui.Output(fmt.Sprintf("Initial Root Token: %s", resp.RootToken)) | ||||||
|   | |||||||
| @@ -244,5 +244,5 @@ func TestInit_PGP(t *testing.T) { | |||||||
|  |  | ||||||
| 	rootToken := matches[0][1] | 	rootToken := matches[0][1] | ||||||
|  |  | ||||||
| 	parseDecryptAndTestUnsealKeys(t, ui.OutputWriter.String(), rootToken, false, nil, core) | 	parseDecryptAndTestUnsealKeys(t, ui.OutputWriter.String(), rootToken, false, nil, nil, core) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -66,7 +66,9 @@ func parseDecryptAndTestUnsealKeys(t *testing.T, | |||||||
| 	input, rootToken string, | 	input, rootToken string, | ||||||
| 	fingerprints bool, | 	fingerprints bool, | ||||||
| 	backupKeys map[string][]string, | 	backupKeys map[string][]string, | ||||||
|  | 	backupKeysB64 map[string][]string, | ||||||
| 	core *vault.Core) { | 	core *vault.Core) { | ||||||
|  |  | ||||||
| 	decoder := base64.StdEncoding | 	decoder := base64.StdEncoding | ||||||
| 	priv1Bytes, err := decoder.DecodeString(pgpkeys.TestPrivKey1) | 	priv1Bytes, err := decoder.DecodeString(pgpkeys.TestPrivKey1) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -87,89 +89,106 @@ func parseDecryptAndTestUnsealKeys(t *testing.T, | |||||||
| 		priv3Bytes, | 		priv3Bytes, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var re *regexp.Regexp | 	testFunc := func(b64 bool, bkeys map[string][]string) { | ||||||
| 	if fingerprints { | 		var re *regexp.Regexp | ||||||
| 		re, err = regexp.Compile("\\s*Key\\s+\\d+\\s+fingerprint:\\s+([0-9a-fA-F]+);\\s+value:\\s+(.*)") |  | ||||||
| 	} else { |  | ||||||
| 		re, err = regexp.Compile("\\s*Key\\s+\\d+:\\s+(.*)") |  | ||||||
| 	} |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("Error compiling regex: %s", err) |  | ||||||
| 	} |  | ||||||
| 	matches := re.FindAllStringSubmatch(input, -1) |  | ||||||
| 	if len(matches) != 4 { |  | ||||||
| 		t.Fatalf("Unexpected number of keys returned, got %d, matches was \n\n%#v\n\n, input was \n\n%s\n\n", len(matches), matches, input) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	encodedKeys := []string{} |  | ||||||
| 	matchedFingerprints := []string{} |  | ||||||
| 	for _, tuple := range matches { |  | ||||||
| 		if fingerprints { | 		if fingerprints { | ||||||
| 			if len(tuple) != 3 { | 			if b64 { | ||||||
| 				t.Fatalf("Key not found: %#v", tuple) | 				re, err = regexp.Compile("\\s*Key\\s+\\d+\\s+fingerprint:\\s+([0-9a-fA-F]+);\\s+value\\s+\\(base64\\):\\s+(.*)") | ||||||
|  | 			} else { | ||||||
|  | 				re, err = regexp.Compile("\\s*Key\\s+\\d+\\s+fingerprint:\\s+([0-9a-fA-F]+);\\s+value\\s+\\(hex\\)\\s+:\\s+(.*)") | ||||||
| 			} | 			} | ||||||
| 			matchedFingerprints = append(matchedFingerprints, tuple[1]) |  | ||||||
| 			encodedKeys = append(encodedKeys, tuple[2]) |  | ||||||
| 		} else { | 		} else { | ||||||
| 			if len(tuple) != 2 { | 			if b64 { | ||||||
| 				t.Fatalf("Key not found: %#v", tuple) | 				re, err = regexp.Compile("\\s*Key\\s+\\d+\\s\\(base64\\):\\s+(.*)") | ||||||
|  | 			} else { | ||||||
|  | 				re, err = regexp.Compile("\\s*Key\\s+\\d+\\s\\(hex\\)\\s+:\\s+(.*)") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("Error compiling regex: %s", err) | ||||||
|  | 		} | ||||||
|  | 		matches := re.FindAllStringSubmatch(input, -1) | ||||||
|  | 		if len(matches) != 4 { | ||||||
|  | 			t.Fatalf("Unexpected number of keys returned, got %d, matches was \n\n%#v\n\n, input was \n\n%s\n\n", len(matches), matches, input) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		encodedKeys := []string{} | ||||||
|  | 		matchedFingerprints := []string{} | ||||||
|  | 		for _, tuple := range matches { | ||||||
|  | 			if fingerprints { | ||||||
|  | 				if len(tuple) != 3 { | ||||||
|  | 					t.Fatalf("Key not found: %#v", tuple) | ||||||
|  | 				} | ||||||
|  | 				matchedFingerprints = append(matchedFingerprints, tuple[1]) | ||||||
|  | 				encodedKeys = append(encodedKeys, tuple[2]) | ||||||
|  | 			} else { | ||||||
|  | 				if len(tuple) != 2 { | ||||||
|  | 					t.Fatalf("Key not found: %#v", tuple) | ||||||
|  | 				} | ||||||
|  | 				encodedKeys = append(encodedKeys, tuple[1]) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if bkeys != nil && len(matchedFingerprints) != 0 { | ||||||
|  | 			testMap := map[string][]string{} | ||||||
|  | 			for i, v := range matchedFingerprints { | ||||||
|  | 				testMap[v] = append(testMap[v], encodedKeys[i]) | ||||||
|  | 				sort.Strings(testMap[v]) | ||||||
|  | 			} | ||||||
|  | 			if !reflect.DeepEqual(testMap, bkeys) { | ||||||
|  | 				t.Fatalf("test map and backup map do not match, test map is\n%#v\nbackup map is\n%#v", testMap, bkeys) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		unsealKeys := []string{} | ||||||
|  | 		ptBuf := bytes.NewBuffer(nil) | ||||||
|  | 		for i, privKeyBytes := range privBytes { | ||||||
|  | 			if i > 2 { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			ptBuf.Reset() | ||||||
|  | 			entity, err := openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(privKeyBytes))) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("Error parsing private key %d: %s", i, err) | ||||||
|  | 			} | ||||||
|  | 			var keyBytes []byte | ||||||
|  | 			if b64 { | ||||||
|  | 				keyBytes, err = base64.StdEncoding.DecodeString(encodedKeys[i]) | ||||||
|  | 			} else { | ||||||
|  | 				keyBytes, err = hex.DecodeString(encodedKeys[i]) | ||||||
|  | 			} | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("Error decoding key %d: %s", i, err) | ||||||
|  | 			} | ||||||
|  | 			entityList := &openpgp.EntityList{entity} | ||||||
|  | 			md, err := openpgp.ReadMessage(bytes.NewBuffer(keyBytes), entityList, nil, nil) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("Error decrypting with key %d (%s): %s", i, encodedKeys[i], err) | ||||||
|  | 			} | ||||||
|  | 			ptBuf.ReadFrom(md.UnverifiedBody) | ||||||
|  | 			unsealKeys = append(unsealKeys, ptBuf.String()) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = core.Seal(rootToken) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("Error sealing vault with provided root token: %s", err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for i, unsealKey := range unsealKeys { | ||||||
|  | 			unsealBytes, err := hex.DecodeString(unsealKey) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("Error hex decoding unseal key %s: %s", unsealKey, err) | ||||||
|  | 			} | ||||||
|  | 			unsealed, err := core.Unseal(unsealBytes) | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatalf("Error using unseal key %s: %s", unsealKey, err) | ||||||
|  | 			} | ||||||
|  | 			if i >= 2 && !unsealed { | ||||||
|  | 				t.Fatalf("Error: Provided two unseal keys but core is not unsealed") | ||||||
| 			} | 			} | ||||||
| 			encodedKeys = append(encodedKeys, tuple[1]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if backupKeys != nil && len(matchedFingerprints) != 0 { |  | ||||||
| 		testMap := map[string][]string{} |  | ||||||
| 		for i, v := range matchedFingerprints { |  | ||||||
| 			testMap[v] = append(testMap[v], encodedKeys[i]) |  | ||||||
| 			sort.Strings(testMap[v]) |  | ||||||
| 		} |  | ||||||
| 		if !reflect.DeepEqual(testMap, backupKeys) { |  | ||||||
| 			t.Fatalf("test map and backup map do not match, test map is\n%#v\nbackup map is\n%#v", testMap, backupKeys) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	unsealKeys := []string{} |  | ||||||
| 	ptBuf := bytes.NewBuffer(nil) |  | ||||||
| 	for i, privKeyBytes := range privBytes { |  | ||||||
| 		if i > 2 { |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 		ptBuf.Reset() |  | ||||||
| 		entity, err := openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(privKeyBytes))) |  | ||||||
| 		if err != nil { |  | ||||||
| 			t.Fatalf("Error parsing private key %d: %s", i, err) |  | ||||||
| 		} |  | ||||||
| 		keyBytes, err := hex.DecodeString(encodedKeys[i]) |  | ||||||
| 		if err != nil { |  | ||||||
| 			t.Fatalf("Error hex-decoding key %d: %s", i, err) |  | ||||||
| 		} |  | ||||||
| 		entityList := &openpgp.EntityList{entity} |  | ||||||
| 		md, err := openpgp.ReadMessage(bytes.NewBuffer(keyBytes), entityList, nil, nil) |  | ||||||
| 		if err != nil { |  | ||||||
| 			t.Fatalf("Error decrypting with key %d (%s): %s", i, encodedKeys[i], err) |  | ||||||
| 		} |  | ||||||
| 		ptBuf.ReadFrom(md.UnverifiedBody) |  | ||||||
| 		unsealKeys = append(unsealKeys, ptBuf.String()) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = core.Seal(rootToken) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("Error sealing vault with provided root token: %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for i, unsealKey := range unsealKeys { |  | ||||||
| 		unsealBytes, err := hex.DecodeString(unsealKey) |  | ||||||
| 		if err != nil { |  | ||||||
| 			t.Fatalf("Error hex decoding unseal key %s: %s", unsealKey, err) |  | ||||||
| 		} |  | ||||||
| 		unsealed, err := core.Unseal(unsealBytes) |  | ||||||
| 		if err != nil { |  | ||||||
| 			t.Fatalf("Error using unseal key %s: %s", unsealKey, err) |  | ||||||
| 		} |  | ||||||
| 		if i >= 2 && !unsealed { |  | ||||||
| 			t.Fatalf("Error: Provided two unseal keys but core is not unsealed") |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	testFunc(false, backupKeys) | ||||||
|  | 	testFunc(true, backupKeysB64) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -93,12 +93,14 @@ func (c *RekeyCommand) Run(args []string) int { | |||||||
| 				SecretShares:    shares, | 				SecretShares:    shares, | ||||||
| 				SecretThreshold: threshold, | 				SecretThreshold: threshold, | ||||||
| 				PGPKeys:         pgpKeys, | 				PGPKeys:         pgpKeys, | ||||||
|  | 				Backup:          backup, | ||||||
| 			}) | 			}) | ||||||
| 		} else { | 		} else { | ||||||
| 			rekeyStatus, err = client.Sys().RekeyInit(&api.RekeyInitRequest{ | 			rekeyStatus, err = client.Sys().RekeyInit(&api.RekeyInitRequest{ | ||||||
| 				SecretShares:    shares, | 				SecretShares:    shares, | ||||||
| 				SecretThreshold: threshold, | 				SecretThreshold: threshold, | ||||||
| 				PGPKeys:         pgpKeys, | 				PGPKeys:         pgpKeys, | ||||||
|  | 				Backup:          backup, | ||||||
| 			}) | 			}) | ||||||
| 		} | 		} | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -158,11 +160,25 @@ func (c *RekeyCommand) Run(args []string) int { | |||||||
| 	// Space between the key prompt, if any, and the output | 	// Space between the key prompt, if any, and the output | ||||||
| 	c.Ui.Output("\n") | 	c.Ui.Output("\n") | ||||||
| 	// Provide the keys | 	// Provide the keys | ||||||
|  | 	var haveB64 bool | ||||||
|  | 	if result.KeysB64 != nil && len(result.KeysB64) == len(result.Keys) { | ||||||
|  | 		haveB64 = true | ||||||
|  | 	} | ||||||
| 	for i, key := range result.Keys { | 	for i, key := range result.Keys { | ||||||
| 		if len(result.PGPFingerprints) > 0 { | 		if len(result.PGPFingerprints) > 0 { | ||||||
| 			c.Ui.Output(fmt.Sprintf("Key %d fingerprint: %s; value: %s", i+1, result.PGPFingerprints[i], key)) | 			if haveB64 { | ||||||
|  | 				c.Ui.Output(fmt.Sprintf("Key %d fingerprint: %s; value (hex)   : %s", i+1, result.PGPFingerprints[i], key)) | ||||||
|  | 				c.Ui.Output(fmt.Sprintf("Key %d fingerprint: %s; value (base64): %s", i+1, result.PGPFingerprints[i], result.KeysB64[i])) | ||||||
|  | 			} else { | ||||||
|  | 				c.Ui.Output(fmt.Sprintf("Key %d fingerprint: %s; value: %s", i+1, result.PGPFingerprints[i], key)) | ||||||
|  | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			c.Ui.Output(fmt.Sprintf("Key %d: %s", i+1, key)) | 			if haveB64 { | ||||||
|  | 				c.Ui.Output(fmt.Sprintf("Key %d (hex)   : %s", i+1, key)) | ||||||
|  | 				c.Ui.Output(fmt.Sprintf("Key %d (base64): %s", i+1, result.KeysB64[i])) | ||||||
|  | 			} else { | ||||||
|  | 				c.Ui.Output(fmt.Sprintf("Key %d: %s", i+1, key)) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -227,7 +227,8 @@ func TestRekey_init_pgp(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	type backupStruct struct { | 	type backupStruct struct { | ||||||
| 		Keys map[string][]string | 		Keys    map[string][]string | ||||||
|  | 		KeysB64 map[string][]string | ||||||
| 	} | 	} | ||||||
| 	backupVals := &backupStruct{} | 	backupVals := &backupStruct{} | ||||||
|  |  | ||||||
| @@ -247,6 +248,7 @@ func TestRekey_init_pgp(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	backupVals.Keys = resp.Data["keys"].(map[string][]string) | 	backupVals.Keys = resp.Data["keys"].(map[string][]string) | ||||||
|  | 	backupVals.KeysB64 = resp.Data["keys_base64"].(map[string][]string) | ||||||
|  |  | ||||||
| 	// Now delete and try again; the values should be inaccessible | 	// Now delete and try again; the values should be inaccessible | ||||||
| 	req = logical.TestRequest(t, logical.DeleteOperation, "rekey/backup") | 	req = logical.TestRequest(t, logical.DeleteOperation, "rekey/backup") | ||||||
| @@ -269,7 +271,8 @@ func TestRekey_init_pgp(t *testing.T) { | |||||||
| 	// Sort, because it'll be tested with DeepEqual later | 	// Sort, because it'll be tested with DeepEqual later | ||||||
| 	for k, _ := range backupVals.Keys { | 	for k, _ := range backupVals.Keys { | ||||||
| 		sort.Strings(backupVals.Keys[k]) | 		sort.Strings(backupVals.Keys[k]) | ||||||
|  | 		sort.Strings(backupVals.KeysB64[k]) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	parseDecryptAndTestUnsealKeys(t, ui.OutputWriter.String(), token, true, backupVals.Keys, core) | 	parseDecryptAndTestUnsealKeys(t, ui.OutputWriter.String(), token, true, backupVals.Keys, backupVals.KeysB64, core) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package command | package command | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/base64" | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log" | 	"log" | ||||||
| @@ -489,8 +490,9 @@ func (c *ServerCommand) Run(args []string) int { | |||||||
| 				"    "+export+" VAULT_ADDR="+quote+"http://"+config.Listeners[0].Config["address"]+quote+"\n\n"+ | 				"    "+export+" VAULT_ADDR="+quote+"http://"+config.Listeners[0].Config["address"]+quote+"\n\n"+ | ||||||
| 				"The unseal key and root token are reproduced below in case you\n"+ | 				"The unseal key and root token are reproduced below in case you\n"+ | ||||||
| 				"want to seal/unseal the Vault or play with authentication.\n\n"+ | 				"want to seal/unseal the Vault or play with authentication.\n\n"+ | ||||||
| 				"Unseal Key: %s\nRoot Token: %s\n", | 				"Unseal Key (hex)   : %s\nUnseal Key (base64): %s\nRoot Token: %s\n", | ||||||
| 			hex.EncodeToString(init.SecretShares[0]), | 			hex.EncodeToString(init.SecretShares[0]), | ||||||
|  | 			base64.StdEncoding.EncodeToString(init.SecretShares[0]), | ||||||
| 			init.RootToken, | 			init.RootToken, | ||||||
| 		)) | 		)) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package http | package http | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/base64" | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -123,13 +124,20 @@ func handleSysGenerateRootUpdate(core *vault.Core) http.Handler { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Decode the key, which is hex encoded | 		// Decode the key, which is base64 or hex encoded | ||||||
|  | 		min, max := core.BarrierKeyLength() | ||||||
| 		key, err := hex.DecodeString(req.Key) | 		key, err := hex.DecodeString(req.Key) | ||||||
| 		if err != nil { | 		// We check min and max here to ensure that a string that is base64 | ||||||
| 			respondError( | 		// encoded but also valid hex will not be valid and we instead base64 | ||||||
| 				w, http.StatusBadRequest, | 		// decode it | ||||||
| 				errors.New("'key' must be a valid hex-string")) | 		if err != nil || len(key) < min || len(key) > max { | ||||||
| 			return | 			key, err = base64.StdEncoding.DecodeString(req.Key) | ||||||
|  | 			if err != nil { | ||||||
|  | 				respondError( | ||||||
|  | 					w, http.StatusBadRequest, | ||||||
|  | 					errors.New("'key' must be a valid hex or base64 string")) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Use the key to make progress on root generation | 		// Use the key to make progress on root generation | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package http | package http | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/base64" | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| @@ -93,19 +94,24 @@ func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request) | |||||||
|  |  | ||||||
| 	// Encode the keys | 	// Encode the keys | ||||||
| 	keys := make([]string, 0, len(result.SecretShares)) | 	keys := make([]string, 0, len(result.SecretShares)) | ||||||
|  | 	keysB64 := make([]string, 0, len(result.SecretShares)) | ||||||
| 	for _, k := range result.SecretShares { | 	for _, k := range result.SecretShares { | ||||||
| 		keys = append(keys, hex.EncodeToString(k)) | 		keys = append(keys, hex.EncodeToString(k)) | ||||||
|  | 		keysB64 = append(keysB64, base64.StdEncoding.EncodeToString(k)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	resp := &InitResponse{ | 	resp := &InitResponse{ | ||||||
| 		Keys:      keys, | 		Keys:      keys, | ||||||
|  | 		KeysB64:   keysB64, | ||||||
| 		RootToken: result.RootToken, | 		RootToken: result.RootToken, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(result.RecoveryShares) > 0 { | 	if len(result.RecoveryShares) > 0 { | ||||||
| 		resp.RecoveryKeys = make([]string, 0, len(result.RecoveryShares)) | 		resp.RecoveryKeys = make([]string, 0, len(result.RecoveryShares)) | ||||||
|  | 		resp.RecoveryKeysB64 = make([]string, 0, len(result.RecoveryShares)) | ||||||
| 		for _, k := range result.RecoveryShares { | 		for _, k := range result.RecoveryShares { | ||||||
| 			resp.RecoveryKeys = append(resp.RecoveryKeys, hex.EncodeToString(k)) | 			resp.RecoveryKeys = append(resp.RecoveryKeys, hex.EncodeToString(k)) | ||||||
|  | 			resp.RecoveryKeysB64 = append(resp.RecoveryKeysB64, base64.StdEncoding.EncodeToString(k)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -125,9 +131,11 @@ type InitRequest struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type InitResponse struct { | type InitResponse struct { | ||||||
| 	Keys         []string `json:"keys"` | 	Keys            []string `json:"keys"` | ||||||
| 	RecoveryKeys []string `json:"recovery_keys,omitempty"` | 	KeysB64         []string `json:"keys_base64"` | ||||||
| 	RootToken    string   `json:"root_token"` | 	RecoveryKeys    []string `json:"recovery_keys,omitempty"` | ||||||
|  | 	RecoveryKeysB64 []string `json:"recovery_keys_base64,omitempty"` | ||||||
|  | 	RootToken       string   `json:"root_token"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type InitStatusResponse struct { | type InitStatusResponse struct { | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package http | package http | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/base64" | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -146,13 +147,20 @@ func handleSysRekeyUpdate(core *vault.Core, recovery bool) http.Handler { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Decode the key, which is hex encoded | 		// Decode the key, which is base64 or hex encoded | ||||||
|  | 		min, max := core.BarrierKeyLength() | ||||||
| 		key, err := hex.DecodeString(req.Key) | 		key, err := hex.DecodeString(req.Key) | ||||||
| 		if err != nil { | 		// We check min and max here to ensure that a string that is base64 | ||||||
| 			respondError( | 		// encoded but also valid hex will not be valid and we instead base64 | ||||||
| 				w, http.StatusBadRequest, | 		// decode it | ||||||
| 				errors.New("'key' must be a valid hex-string")) | 		if err != nil || len(key) < min || len(key) > max { | ||||||
| 			return | 			key, err = base64.StdEncoding.DecodeString(req.Key) | ||||||
|  | 			if err != nil { | ||||||
|  | 				respondError( | ||||||
|  | 					w, http.StatusBadRequest, | ||||||
|  | 					errors.New("'key' must be a valid hex or base64 string")) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Use the key to make progress on rekey | 		// Use the key to make progress on rekey | ||||||
| @@ -167,16 +175,18 @@ func handleSysRekeyUpdate(core *vault.Core, recovery bool) http.Handler { | |||||||
| 		if result != nil { | 		if result != nil { | ||||||
| 			resp.Complete = true | 			resp.Complete = true | ||||||
| 			resp.Nonce = req.Nonce | 			resp.Nonce = req.Nonce | ||||||
|  | 			resp.Backup = result.Backup | ||||||
|  | 			resp.PGPFingerprints = result.PGPFingerprints | ||||||
|  |  | ||||||
| 			// Encode the keys | 			// Encode the keys | ||||||
| 			keys := make([]string, 0, len(result.SecretShares)) | 			keys := make([]string, 0, len(result.SecretShares)) | ||||||
|  | 			keysB64 := make([]string, 0, len(result.SecretShares)) | ||||||
| 			for _, k := range result.SecretShares { | 			for _, k := range result.SecretShares { | ||||||
| 				keys = append(keys, hex.EncodeToString(k)) | 				keys = append(keys, hex.EncodeToString(k)) | ||||||
|  | 				keysB64 = append(keysB64, base64.StdEncoding.EncodeToString(k)) | ||||||
| 			} | 			} | ||||||
| 			resp.Keys = keys | 			resp.Keys = keys | ||||||
|  | 			resp.KeysB64 = keysB64 | ||||||
| 			resp.Backup = result.Backup |  | ||||||
| 			resp.PGPFingerprints = result.PGPFingerprints |  | ||||||
| 		} | 		} | ||||||
| 		respondOk(w, resp) | 		respondOk(w, resp) | ||||||
| 	}) | 	}) | ||||||
| @@ -210,6 +220,7 @@ type RekeyUpdateResponse struct { | |||||||
| 	Nonce           string   `json:"nonce"` | 	Nonce           string   `json:"nonce"` | ||||||
| 	Complete        bool     `json:"complete"` | 	Complete        bool     `json:"complete"` | ||||||
| 	Keys            []string `json:"keys"` | 	Keys            []string `json:"keys"` | ||||||
|  | 	KeysB64         []string `json:"keys_base64"` | ||||||
| 	PGPFingerprints []string `json:"pgp_fingerprints"` | 	PGPFingerprints []string `json:"pgp_fingerprints"` | ||||||
| 	Backup          bool     `json:"backup"` | 	Backup          bool     `json:"backup"` | ||||||
| } | } | ||||||
|   | |||||||
| @@ -180,8 +180,13 @@ func TestSysRekey_Update(t *testing.T) { | |||||||
| 	if len(keys) != 5 { | 	if len(keys) != 5 { | ||||||
| 		t.Fatalf("bad: %#v", keys) | 		t.Fatalf("bad: %#v", keys) | ||||||
| 	} | 	} | ||||||
|  | 	keysB64 := actual["keys_base64"].([]interface{}) | ||||||
|  | 	if len(keysB64) != 5 { | ||||||
|  | 		t.Fatalf("bad: %#v", keysB64) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	delete(actual, "keys") | 	delete(actual, "keys") | ||||||
|  | 	delete(actual, "keys_base64") | ||||||
| 	if !reflect.DeepEqual(actual, expected) { | 	if !reflect.DeepEqual(actual, expected) { | ||||||
| 		t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) | 		t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package http | package http | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/base64" | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -97,13 +98,20 @@ func handleSysUnseal(core *vault.Core) http.Handler { | |||||||
| 			} | 			} | ||||||
| 			core.ResetUnsealProcess() | 			core.ResetUnsealProcess() | ||||||
| 		} else { | 		} else { | ||||||
| 			// Decode the key, which is hex encoded | 			// Decode the key, which is base64 or hex encoded | ||||||
|  | 			min, max := core.BarrierKeyLength() | ||||||
| 			key, err := hex.DecodeString(req.Key) | 			key, err := hex.DecodeString(req.Key) | ||||||
| 			if err != nil { | 			// We check min and max here to ensure that a string that is base64 | ||||||
| 				respondError( | 			// encoded but also valid hex will not be valid and we instead base64 | ||||||
| 					w, http.StatusBadRequest, | 			// decode it | ||||||
| 					errors.New("'key' must be a valid hex-string")) | 			if err != nil || len(key) < min || len(key) > max { | ||||||
| 				return | 				key, err = base64.StdEncoding.DecodeString(req.Key) | ||||||
|  | 				if err != nil { | ||||||
|  | 					respondError( | ||||||
|  | 						w, http.StatusBadRequest, | ||||||
|  | 						errors.New("'key' must be a valid hex or base64 string")) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Attempt the unseal | 			// Attempt the unseal | ||||||
|   | |||||||
| @@ -1490,3 +1490,9 @@ func (c *Core) SealAccess() *SealAccess { | |||||||
| func (c *Core) Logger() *log.Logger { | func (c *Core) Logger() *log.Logger { | ||||||
| 	return c.logger | 	return c.logger | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *Core) BarrierKeyLength() (min, max int) { | ||||||
|  | 	min, max = c.barrier.KeyLength() | ||||||
|  | 	max += shamir.ShareOverhead | ||||||
|  | 	return | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| package vault | package vault | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"encoding/hex" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| @@ -607,11 +609,28 @@ func (b *SystemBackend) handleRekeyRetrieve( | |||||||
| 		return logical.ErrorResponse("no backed-up keys found"), nil | 		return logical.ErrorResponse("no backed-up keys found"), nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	keysB64 := map[string][]string{} | ||||||
|  | 	for k, v := range backup.Keys { | ||||||
|  | 		for _, j := range v { | ||||||
|  | 			currB64Keys := keysB64[k] | ||||||
|  | 			if currB64Keys == nil { | ||||||
|  | 				currB64Keys = []string{} | ||||||
|  | 			} | ||||||
|  | 			key, err := hex.DecodeString(j) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, fmt.Errorf("error decoding hex-encoded backup key: %v", err) | ||||||
|  | 			} | ||||||
|  | 			currB64Keys = append(currB64Keys, base64.StdEncoding.EncodeToString(key)) | ||||||
|  | 			keysB64[k] = currB64Keys | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Format the status | 	// Format the status | ||||||
| 	resp := &logical.Response{ | 	resp := &logical.Response{ | ||||||
| 		Data: map[string]interface{}{ | 		Data: map[string]interface{}{ | ||||||
| 			"nonce": backup.Nonce, | 			"nonce":       backup.Nonce, | ||||||
| 			"keys":  backup.Keys, | 			"keys":        backup.Keys, | ||||||
|  | 			"keys_base64": keysB64, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jeff Mitchell
					Jeff Mitchell