This repository has been archived by the owner on Feb 16, 2023. It is now read-only.
/
encrypted_data.go
272 lines (246 loc) · 7.52 KB
/
encrypted_data.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
package api
import (
"encoding/json"
"strings"
)
// Errors
var (
ErrInvalidEncryptionAlgorithm = errAPI.Code("invalid_encryption_algorithm").Error("invalid encryption algorithm provided")
ErrInvalidKeyType = errAPI.Code("invalid_key_type").Error("invalid key type")
ErrKeyAlgorithmMismatch = errAPI.Code("key_algorithm_mismatch").Error("mismatch between algorithm and key type")
ErrInvalidKeyLength = errAPI.Code("invalid_key_length").Error("key length value is invalid")
ErrInvalidKeyDerivationAlgorithm = errAPI.Code("invalid_key_derivation_algorithm").Error("invalid key derivation algorithm")
)
// EncryptionAlgorithm specifies the encryption algorithm used for EncryptedData.
type EncryptionAlgorithm string
// UnmarshalJSON populates an EncryptionAlgorithm by converting an input string to lowercase.
func (ed *EncryptionAlgorithm) UnmarshalJSON(b []byte) error {
var v string
err := json.Unmarshal(b, &v)
if err != nil {
return err
}
*ed = EncryptionAlgorithm(strings.ToLower(v))
return nil
}
// HashingAlgorithm specifies the hashing algorithm used for any encryption algorithm using hasing.
type HashingAlgorithm string
// UnmarshalJSON populates an HashingAlgorithm by converting an input string to lowercase.
func (ed *HashingAlgorithm) UnmarshalJSON(b []byte) error {
var v string
err := json.Unmarshal(b, &v)
if err != nil {
return err
}
*ed = HashingAlgorithm(strings.ToLower(v))
return nil
}
// Supported values for EncryptionAlgorithm.
const (
EncryptionAlgorithmAESGCM EncryptionAlgorithm = "aes-gcm"
EncryptionAlgorithmRSAOEAP EncryptionAlgorithm = "rsa-oaep"
EncryptionAlgorithmAWSKMS EncryptionAlgorithm = "aws-kms"
EncryptionAlgorithmGCPKMS EncryptionAlgorithm = "gcp-kms"
HashingAlgorithmSHA256 HashingAlgorithm = "sha-256"
)
// EncryptedData contains data that is encrypted with an algorithm described by Algorithm.
// If the encryption method requires metadata, this is contained in Metadata.
type EncryptedData struct {
Algorithm EncryptionAlgorithm `json:"algorithm"`
Key interface{} `json:"key"`
Parameters interface{} `json:"parameters,omitempty"`
Metadata interface{} `json:"metadata,omitempty"`
Ciphertext []byte `json:"ciphertext"`
}
// NewEncryptedDataAESGCM creates a new EncryptedData with the AES-GCM algorithm.
func NewEncryptedDataAESGCM(ciphertext, nonce []byte, nonceLength int, key interface{}) *EncryptedData {
return &EncryptedData{
Algorithm: EncryptionAlgorithmAESGCM,
Key: key,
Metadata: &EncryptionMetadataAESGCM{
Nonce: nonce,
},
Parameters: &EncryptionParametersAESGCM{
NonceLength: nonceLength,
},
Ciphertext: ciphertext,
}
}
// NewEncryptedDataRSAOAEP creates a new EncryptedData with the RSA-OAEP algorithm.
func NewEncryptedDataRSAOAEP(ciphertext []byte, hashingAlgorithm HashingAlgorithm, key interface{}) *EncryptedData {
return &EncryptedData{
Algorithm: EncryptionAlgorithmRSAOEAP,
Key: key,
Metadata: nil,
Parameters: &EncryptionParametersRSAOAEP{
HashingAlgorithm: hashingAlgorithm,
},
Ciphertext: ciphertext,
}
}
// NewEncryptedDataAWSKMS creates a new EncryptedData with the AWS-KMS algorithm.
func NewEncryptedDataAWSKMS(ciphertext []byte, key *EncryptionKeyAWS) *EncryptedData {
return &EncryptedData{
Algorithm: EncryptionAlgorithmAWSKMS,
Key: key,
Metadata: nil,
Parameters: nil,
Ciphertext: ciphertext,
}
}
// NewEncryptedDataAWSKMS creates a new EncryptedData with the GCP-KMS algorithm.
func NewEncryptedDataGCPKMS(ciphertext []byte, key *EncryptionKeyGCP) *EncryptedData {
return &EncryptedData{
Algorithm: EncryptionAlgorithmGCPKMS,
Key: key,
Metadata: nil,
Parameters: nil,
Ciphertext: ciphertext,
}
}
// UnmarshalJSON populates an EncryptedData from a JSON representation.
func (ed *EncryptedData) UnmarshalJSON(b []byte) error {
// Declare a private type to avoid recursion into this function.
type encryptedData EncryptedData
var rawKey, rawParameters, rawMetadata json.RawMessage
dec := encryptedData{
Key: &rawKey,
Parameters: &rawParameters,
Metadata: &rawMetadata,
}
err := json.Unmarshal(b, &dec)
if err != nil {
return err
}
if rawKey == nil {
return ErrInvalidKeyType
}
var keyType struct {
Type KeyType `json:"type"`
}
err = json.Unmarshal(rawKey, &keyType)
if err != nil {
return err
}
switch keyType.Type {
case KeyTypeDerived:
dec.Key = &EncryptionKeyDerived{}
case KeyTypeEncrypted:
dec.Key = &EncryptionKeyEncrypted{}
case KeyTypeLocal:
dec.Key = &EncryptionKeyLocal{}
case KeyTypeBootstrapCode:
dec.Key = &EncryptionKeyBootstrapCode{}
case KeyTypeAccountKey:
dec.Key = &EncryptionKeyAccountKey{}
case KeyTypeSecretKey:
dec.Key = &EncryptionKeySecretKey{}
case KeyTypeAWS:
dec.Key = &EncryptionKeyAWS{}
case KeyTypeGCP:
dec.Key = &EncryptionKeyGCP{}
default:
return ErrInvalidKeyType
}
err = json.Unmarshal(rawKey, dec.Key)
if err != nil {
return err
}
switch dec.Algorithm {
case EncryptionAlgorithmRSAOEAP:
dec.Metadata = nil
dec.Parameters = &EncryptionParametersRSAOAEP{}
case EncryptionAlgorithmAESGCM:
dec.Metadata = &EncryptionMetadataAESGCM{}
dec.Parameters = &EncryptionParametersAESGCM{}
case EncryptionAlgorithmAWSKMS:
dec.Metadata = nil
dec.Parameters = nil
case EncryptionAlgorithmGCPKMS:
dec.Metadata = nil
dec.Parameters = nil
default:
return ErrInvalidEncryptionAlgorithm
}
if rawMetadata != nil && dec.Metadata != nil {
err = json.Unmarshal(rawMetadata, dec.Metadata)
if err != nil {
return err
}
}
if rawParameters != nil && dec.Parameters != nil {
err = json.Unmarshal(rawParameters, dec.Parameters)
if err != nil {
return err
}
}
*ed = EncryptedData(dec)
return nil
}
type keyValidator interface {
validator
SupportsAlgorithm(EncryptionAlgorithm) bool
}
// Validate whether the EncryptedData is valid.
func (ed *EncryptedData) Validate() error {
if ed.Algorithm != EncryptionAlgorithmAESGCM &&
ed.Algorithm != EncryptionAlgorithmRSAOEAP &&
ed.Algorithm != EncryptionAlgorithmAWSKMS &&
ed.Algorithm != EncryptionAlgorithmGCPKMS {
return ErrInvalidEncryptionAlgorithm
}
if ed.Key == nil {
return ErrMissingField("key")
}
if ed.Ciphertext == nil {
return ErrMissingField("ciphertext")
}
key := ed.Key.(keyValidator)
if err := key.Validate(); err != nil {
return err
}
if !key.SupportsAlgorithm(ed.Algorithm) {
return ErrKeyAlgorithmMismatch
}
parameters, ok := ed.Parameters.(validator)
if ok {
if err := parameters.Validate(); err != nil {
return err
}
}
metadata, ok := ed.Metadata.(validator)
if ok {
if err := metadata.Validate(); err != nil {
return err
}
}
return nil
}
// EncryptedDataAESGCM is a typed EncryptedData for the AESGCM algorithm.
type EncryptedDataAESGCM struct {
Key interface{}
Parameters EncryptionParametersAESGCM
Metadata EncryptionMetadataAESGCM
Ciphertext []byte
}
// AESGCM casts the EncryptedData to EncryptedDataAESGCM.
// Returns an error if the EncryptedData does not have AESGCM as its algorithm.
func (ed *EncryptedData) AESGCM() (*EncryptedDataAESGCM, error) {
if ed.Algorithm != EncryptionAlgorithmAESGCM {
return nil, ErrInvalidEncryptionAlgorithm
}
parameters, ok := ed.Parameters.(*EncryptionParametersAESGCM)
if !ok {
return nil, ErrInvalidEncryptionAlgorithm
}
metadata, ok := ed.Metadata.(*EncryptionMetadataAESGCM)
if !ok {
return nil, ErrInvalidEncryptionAlgorithm
}
return &EncryptedDataAESGCM{
Key: ed.Key,
Parameters: *parameters,
Metadata: *metadata,
Ciphertext: ed.Ciphertext,
}, nil
}