-
Notifications
You must be signed in to change notification settings - Fork 280
/
kek.go
173 lines (145 loc) · 5.76 KB
/
kek.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package cryptutil
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/nacl/box"
"github.com/pomerium/pomerium/pkg/encoding/base58"
)
// A KeyEncryptionKey (KEK) is used to implement *envelope encryption*, similar to how data is stored at rest with
// AWS or Google Cloud:
//
// - AWS: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#enveloping
// - Google Cloud: https://cloud.google.com/kms/docs/envelope-encryption
//
// Data is encrypted with a data encryption key (DEK) and that key is stored next to the data encrypted with the KEK.
// Finally the KEK id is also stored with the data.
//
// To decrypt the data you first retrieve the KEK, second decrypt the DEK, and finally decrypt the data using the DEK.
//
// - Our KEKs are asymmetric Curve25519 keys. We use the *public* key to encrypt the DEK so only the *private* key can
// decrypt it.
// - Our DEKs are symmetric XChaCha20Poly1305 keys.
type KeyEncryptionKey interface {
ID() string
KeyBytes() []byte
isKeyEncryptionKey()
}
// KeyEncryptionKeySize is the size of a key encryption key.
const KeyEncryptionKeySize = curve25519.ScalarSize
// PrivateKeyEncryptionKey is a Curve25519 asymmetric private encryption key used to decrypt data encryption keys.
type PrivateKeyEncryptionKey struct {
data [KeyEncryptionKeySize]byte
}
func (*PrivateKeyEncryptionKey) isKeyEncryptionKey() {}
// NewPrivateKeyEncryptionKey creates a new encryption key from existing bytes.
func NewPrivateKeyEncryptionKey(raw []byte) (*PrivateKeyEncryptionKey, error) {
if len(raw) != KeyEncryptionKeySize {
return nil, fmt.Errorf("cryptutil: invalid key encryption key, expected %d bytes, got %d",
KeyEncryptionKeySize, len(raw))
}
kek := new(PrivateKeyEncryptionKey)
copy(kek.data[:], raw)
return kek, nil
}
// GenerateKeyEncryptionKey generates a new random key encryption key.
func GenerateKeyEncryptionKey() (*PrivateKeyEncryptionKey, error) {
raw := randomBytes(KeyEncryptionKeySize)
return NewPrivateKeyEncryptionKey(raw)
}
// GetKeyEncryptionKeyID derives an id from the key encryption key data itself.
func GetKeyEncryptionKeyID(raw []byte) string {
return base58.Encode(Hash("KeyEncryptionKey", raw))
}
// Decrypt decrypts data from a NACL anonymous box.
func (kek *PrivateKeyEncryptionKey) Decrypt(ciphertext []byte) ([]byte, error) {
private := kek
public := kek.Public()
opened, ok := box.OpenAnonymous(nil, ciphertext, &public.data, &private.data)
if !ok {
return nil, fmt.Errorf("cryptutil: anonymous box decrypt failed")
}
return opened, nil
}
// DecryptDataEncryptionKey decrypts a data encryption key.
func (kek *PrivateKeyEncryptionKey) DecryptDataEncryptionKey(ciphertext []byte) (*DataEncryptionKey, error) {
raw, err := kek.Decrypt(ciphertext)
if err != nil {
return nil, err
}
return NewDataEncryptionKey(raw)
}
// ID returns the private key's id.
func (kek *PrivateKeyEncryptionKey) ID() string {
return kek.Public().id
}
// KeyBytes returns the private key encryption key's raw bytes.
func (kek *PrivateKeyEncryptionKey) KeyBytes() []byte {
data := make([]byte, KeyEncryptionKeySize)
copy(data, kek.data[:])
return data
}
// Public returns the private key's public key.
func (kek *PrivateKeyEncryptionKey) Public() *PublicKeyEncryptionKey {
// taken from NACL box.GenerateKey
var publicKey [32]byte
curve25519.ScalarBaseMult(&publicKey, &kek.data)
return &PublicKeyEncryptionKey{
id: GetKeyEncryptionKeyID(kek.data[:]),
data: publicKey,
}
}
// PublicKeyEncryptionKey is a Curve25519 asymmetric public encryption key used to encrypt data encryption keys.
type PublicKeyEncryptionKey struct {
id string
data [KeyEncryptionKeySize]byte
}
func (*PublicKeyEncryptionKey) isKeyEncryptionKey() {}
// NewPublicKeyEncryptionKey creates a new encryption key from existing bytes.
func NewPublicKeyEncryptionKey(raw []byte) (*PublicKeyEncryptionKey, error) {
return NewPublicKeyEncryptionKeyWithID(GetKeyEncryptionKeyID(raw), raw)
}
// NewPublicKeyEncryptionKeyWithID creates a new encryption key from an existing id and bytes.
func NewPublicKeyEncryptionKeyWithID(id string, raw []byte) (*PublicKeyEncryptionKey, error) {
if len(raw) != KeyEncryptionKeySize {
return nil, fmt.Errorf("cryptutil: invalid key encryption key, expected %d bytes, got %d",
KeyEncryptionKeySize, len(raw))
}
kek := &PublicKeyEncryptionKey{
id: id,
}
copy(kek.data[:], raw)
return kek, nil
}
// ID returns the public key's id.
func (kek *PublicKeyEncryptionKey) ID() string {
return kek.id
}
// KeyBytes returns the public key's raw bytes.
func (kek *PublicKeyEncryptionKey) KeyBytes() []byte {
data := make([]byte, KeyEncryptionKeySize)
copy(data, kek.data[:])
return data
}
// Encrypt encrypts data using a NACL anonymous box.
func (kek *PublicKeyEncryptionKey) Encrypt(plaintext []byte) ([]byte, error) {
sealed, err := box.SealAnonymous(nil, plaintext, &kek.data, rand.Reader)
if err != nil { // only fails on rand.Read errors
return nil, fmt.Errorf("cryptutil: anonymous box encrypt failed: %w", err)
}
return sealed, nil
}
// EncryptDataEncryptionKey encrypts a DataEncryptionKey.
func (kek *PublicKeyEncryptionKey) EncryptDataEncryptionKey(dek *DataEncryptionKey) ([]byte, error) {
return kek.Encrypt(dek.data[:])
}
// A KeyEncryptionKeySource gets private key encryption keys based on their id.
type KeyEncryptionKeySource interface {
GetKeyEncryptionKey(id string) (*PrivateKeyEncryptionKey, error)
}
// A KeyEncryptionKeySourceFunc implements the KeyEncryptionKeySource interface using a function.
type KeyEncryptionKeySourceFunc func(id string) (*PrivateKeyEncryptionKey, error)
// GetKeyEncryptionKey gets the key encryption key by calling the underlying function.
func (src KeyEncryptionKeySourceFunc) GetKeyEncryptionKey(id string) (*PrivateKeyEncryptionKey, error) {
return src(id)
}