package transit import ( "encoding/base64" "fmt" "github.com/hashicorp/vault/helper/errutil" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) func (b *backend) pathRewrap() *framework.Path { return &framework.Path{ Pattern: "rewrap/" + framework.GenericNameRegex("name"), Fields: map[string]*framework.FieldSchema{ "name": &framework.FieldSchema{ Type: framework.TypeString, Description: "Name of the key", }, "ciphertext": &framework.FieldSchema{ Type: framework.TypeString, Description: "Ciphertext value to rewrap", }, "context": &framework.FieldSchema{ Type: framework.TypeString, Description: "Context for key derivation. Required for derived keys.", }, "nonce": &framework.FieldSchema{ Type: framework.TypeString, Description: "Nonce for when convergent encryption is used", }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ logical.UpdateOperation: b.pathRewrapWrite, }, HelpSynopsis: pathRewrapHelpSyn, HelpDescription: pathRewrapHelpDesc, } } func (b *backend) pathRewrapWrite( req *logical.Request, d *framework.FieldData) (*logical.Response, error) { name := d.Get("name").(string) value := d.Get("ciphertext").(string) if len(value) == 0 { return logical.ErrorResponse("missing ciphertext to decrypt"), logical.ErrInvalidRequest } var err error // Decode the context if any contextRaw := d.Get("context").(string) var context []byte if len(contextRaw) != 0 { context, err = base64.StdEncoding.DecodeString(contextRaw) if err != nil { return logical.ErrorResponse("failed to base64-decode context"), logical.ErrInvalidRequest } } // Decode the nonce if any nonceRaw := d.Get("nonce").(string) var nonce []byte if len(nonceRaw) != 0 { nonce, err = base64.StdEncoding.DecodeString(nonceRaw) if err != nil { return logical.ErrorResponse("failed to base64-decode nonce"), logical.ErrInvalidRequest } } // Get the policy p, lock, err := b.lm.GetPolicyShared(req.Storage, name) if lock != nil { defer lock.RUnlock() } if err != nil { return nil, err } // Error if invalid policy if p == nil { return logical.ErrorResponse("policy not found"), logical.ErrInvalidRequest } plaintext, err := p.Decrypt(context, nonce, value) if err != nil { switch err.(type) { case errutil.UserError: return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest case errutil.InternalError: return nil, err default: return nil, err } } if plaintext == "" { return nil, fmt.Errorf("empty plaintext returned during rewrap") } ciphertext, err := p.Encrypt(context, nonce, plaintext) if err != nil { switch err.(type) { case errutil.UserError: return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest case errutil.InternalError: return nil, err default: return nil, err } } if ciphertext == "" { return nil, fmt.Errorf("empty ciphertext returned") } // Generate the response resp := &logical.Response{ Data: map[string]interface{}{ "ciphertext": ciphertext, }, } return resp, nil } const pathRewrapHelpSyn = `Rewrap ciphertext` const pathRewrapHelpDesc = ` After key rotation, this function can be used to rewrap the given ciphertext with the latest version of the named key. If the given ciphertext is already using the latest version of the key, this function is a no-op. `