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:
Jeff Mitchell
2016-08-15 16:01:15 -04:00
committed by GitHub
parent 98d6218cfc
commit ed48b008ce
15 changed files with 236 additions and 117 deletions

View File

@@ -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"`
} }

View File

@@ -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"`
} }

View File

@@ -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))

View File

@@ -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)
} }

View File

@@ -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)
} }

View File

@@ -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))
}
} }
} }

View File

@@ -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)
} }

View File

@@ -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,
)) ))
} }

View File

@@ -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

View File

@@ -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 {

View File

@@ -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"`
} }

View File

@@ -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)
} }

View File

@@ -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

View File

@@ -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
}

View File

@@ -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,
}, },
} }