/
path_show_session_key.go
130 lines (118 loc) · 3.75 KB
/
path_show_session_key.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
package gpg
import (
"bytes"
"context"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"strings"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
)
func pathShowSessionKey(b *backend) *framework.Path {
return &framework.Path{
Pattern: "show-session-key/" + framework.GenericNameRegex("name"),
Fields: map[string]*framework.FieldSchema{
"name": {
Type: framework.TypeString,
Description: "The key to use",
},
"ciphertext": {
Type: framework.TypeString,
Description: "The ciphertext to decrypt",
},
"format": {
Type: framework.TypeString,
Default: "base64",
Description: `Encoding format the ciphertext uses. Can be "base64" or "ascii-armor". Defaults to "base64".`,
},
"signer_key": {
Type: framework.TypeString,
Description: "The ASCII-armored GPG key of the signer of the ciphertext. If present, the signature must be valid.",
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathShowSessionKeyWrite,
},
},
HelpSynopsis: pathDecryptSessionKeyHelpSyn,
HelpDescription: pathDecryptSessionKeyHelpDesc,
}
}
func (b *backend) pathShowSessionKeyWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
format := data.Get("format").(string)
switch format {
case "base64":
case "ascii-armor":
default:
return logical.ErrorResponse(fmt.Sprintf("unsupported encoding format %s; must be \"base64\" or \"ascii-armor\"", format)), nil
}
keyEntry, err := b.key(ctx, req.Storage, data.Get("name").(string))
if err != nil {
return nil, err
}
if keyEntry == nil {
return logical.ErrorResponse("key not found"), logical.ErrInvalidRequest
}
r := bytes.NewReader(keyEntry.SerializedKey)
keyring, err := openpgp.ReadKeyRing(r)
if err != nil {
return nil, err
}
signerKey := data.Get("signer_key").(string)
if signerKey != "" {
el, err := openpgp.ReadArmoredKeyRing(strings.NewReader(signerKey))
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
keyring = append(keyring, el[0])
}
ciphertextEncoded := strings.NewReader(data.Get("ciphertext").(string))
var ciphertextDecoder io.Reader
switch format {
case "base64":
ciphertextDecoder = base64.NewDecoder(base64.StdEncoding, ciphertextEncoded)
case "ascii-armor":
block, err := armor.Decode(ciphertextEncoded)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
ciphertextDecoder = block.Body
}
var p packet.Packet
var sessionKey string
for {
p, err = packet.Read(ciphertextDecoder)
if err == io.EOF {
return logical.ErrorResponse("Unable to decrypt session key"), nil
}
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
switch p := p.(type) {
case *packet.EncryptedKey:
encryptedKey := *p
keys := keyring.KeysById(encryptedKey.KeyId)
for _, key := range keys {
encryptedKey.Decrypt(key.PrivateKey, nil)
if encryptedKey.Key != nil && len(encryptedKey.Key) > 0 {
sessionKey = fmt.Sprintf("%d:%s", encryptedKey.CipherFunc, strings.ToUpper(hex.EncodeToString(encryptedKey.Key)))
return &logical.Response{
Data: map[string]interface{}{
"session_key": sessionKey,
},
}, nil
}
}
}
}
}
const pathDecryptSessionKeyHelpSyn = "Decrypt a session key of a message using a named GPG key"
const pathDecryptSessionKeyHelpDesc = `
This path uses the named GPG key from the request path to decrypt the session key of a message.
`