forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
/
path_datakey.go
142 lines (121 loc) · 3.63 KB
/
path_datakey.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package transit
import (
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/hashicorp/vault/helper/certutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
func pathDatakey() *framework.Path {
return &framework.Path{
Pattern: "datakey/" + framework.GenericNameRegex("plaintext") + "/" + framework.GenericNameRegex("name"),
Fields: map[string]*framework.FieldSchema{
"name": &framework.FieldSchema{
Type: framework.TypeString,
Description: "The backend key used for encrypting the data key",
},
"plaintext": &framework.FieldSchema{
Type: framework.TypeString,
Description: `"plaintext" will return the key in both plaintext and
ciphertext; "wrapped" will return the ciphertext only.`,
},
"context": &framework.FieldSchema{
Type: framework.TypeString,
Description: "Context for key derivation. Required for derived keys.",
},
"bits": &framework.FieldSchema{
Type: framework.TypeInt,
Description: `Number of bits for the key; currently 128, 256,
and 512 bits are supported. Defaults to 256.`,
Default: 256,
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.WriteOperation: pathDatakeyWrite,
},
HelpSynopsis: pathDatakeyHelpSyn,
HelpDescription: pathDatakeyHelpDesc,
}
}
func pathDatakeyWrite(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
name := d.Get("name").(string)
plaintext := d.Get("plaintext").(string)
plaintextAllowed := false
switch plaintext {
case "plaintext":
plaintextAllowed = true
case "wrapped":
default:
return logical.ErrorResponse("Invalid path, must be 'plaintext' or 'wrapped'"), logical.ErrInvalidRequest
}
// Decode the context if any
contextRaw := d.Get("context").(string)
var context []byte
if len(contextRaw) != 0 {
var err error
context, err = base64.StdEncoding.DecodeString(contextRaw)
if err != nil {
return logical.ErrorResponse("failed to decode context as base64"), logical.ErrInvalidRequest
}
}
// Get the policy
p, err := getPolicy(req, name)
if err != nil {
return nil, err
}
// Error if invalid policy
if p == nil {
return logical.ErrorResponse("policy not found"), logical.ErrInvalidRequest
}
newKey := make([]byte, 32)
bits := d.Get("bits").(int)
switch bits {
case 512:
newKey = make([]byte, 64)
case 256:
case 128:
newKey = make([]byte, 16)
default:
return logical.ErrorResponse("invalid bit length"), logical.ErrInvalidRequest
}
_, err = rand.Read(newKey)
if err != nil {
return nil, err
}
ciphertext, err := p.Encrypt(context, base64.StdEncoding.EncodeToString(newKey))
if err != nil {
switch err.(type) {
case certutil.UserError:
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
case certutil.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,
},
}
if plaintextAllowed {
resp.Data["plaintext"] = base64.StdEncoding.EncodeToString(newKey)
}
return resp, nil
}
const pathDatakeyHelpSyn = `Generate a data key`
const pathDatakeyHelpDesc = `
This path can be used to generate a data key: a random
key of a certain length that can be used for encryption
and decryption, protected by the named backend key. 128, 256,
or 512 bits can be specified; if not specified, the default
is 256 bits. Call with the the "wrapped" path to prevent the
(base64-encoded) plaintext key from being returned along with
the encrypted key, the "plaintext" path returns both.
`