-
Notifications
You must be signed in to change notification settings - Fork 9.6k
/
crypto.go
333 lines (294 loc) · 10.9 KB
/
crypto.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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
package funcs
import (
"crypto/md5"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/asn1"
"encoding/base64"
"encoding/hex"
"fmt"
"hash"
"io"
"strings"
uuidv5 "github.com/google/uuid"
uuid "github.com/hashicorp/go-uuid"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
"github.com/zclconf/go-cty/cty/gocty"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/ssh"
)
var UUIDFunc = function.New(&function.Spec{
Params: []function.Parameter{},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
result, err := uuid.GenerateUUID()
if err != nil {
return cty.UnknownVal(cty.String), err
}
return cty.StringVal(result), nil
},
})
var UUIDV5Func = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "namespace",
Type: cty.String,
},
{
Name: "name",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
var namespace uuidv5.UUID
switch {
case args[0].AsString() == "dns":
namespace = uuidv5.NameSpaceDNS
case args[0].AsString() == "url":
namespace = uuidv5.NameSpaceURL
case args[0].AsString() == "oid":
namespace = uuidv5.NameSpaceOID
case args[0].AsString() == "x500":
namespace = uuidv5.NameSpaceX500
default:
if namespace, err = uuidv5.Parse(args[0].AsString()); err != nil {
return cty.UnknownVal(cty.String), fmt.Errorf("uuidv5() doesn't support namespace %s (%v)", args[0].AsString(), err)
}
}
val := args[1].AsString()
return cty.StringVal(uuidv5.NewSHA1(namespace, []byte(val)).String()), nil
},
})
// Base64Sha256Func constructs a function that computes the SHA256 hash of a given string
// and encodes it with Base64.
var Base64Sha256Func = makeStringHashFunction(sha256.New, base64.StdEncoding.EncodeToString)
// MakeFileBase64Sha256Func constructs a function that is like Base64Sha256Func but reads the
// contents of a file rather than hashing a given literal string.
func MakeFileBase64Sha256Func(baseDir string) function.Function {
return makeFileHashFunction(baseDir, sha256.New, base64.StdEncoding.EncodeToString)
}
// Base64Sha512Func constructs a function that computes the SHA256 hash of a given string
// and encodes it with Base64.
var Base64Sha512Func = makeStringHashFunction(sha512.New, base64.StdEncoding.EncodeToString)
// MakeFileBase64Sha512Func constructs a function that is like Base64Sha512Func but reads the
// contents of a file rather than hashing a given literal string.
func MakeFileBase64Sha512Func(baseDir string) function.Function {
return makeFileHashFunction(baseDir, sha512.New, base64.StdEncoding.EncodeToString)
}
// BcryptFunc constructs a function that computes a hash of the given string using the Blowfish cipher.
var BcryptFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
},
VarParam: &function.Parameter{
Name: "cost",
Type: cty.Number,
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
defaultCost := 10
if len(args) > 1 {
var val int
if err := gocty.FromCtyValue(args[1], &val); err != nil {
return cty.UnknownVal(cty.String), err
}
defaultCost = val
}
if len(args) > 2 {
return cty.UnknownVal(cty.String), fmt.Errorf("bcrypt() takes no more than two arguments")
}
input := args[0].AsString()
out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost)
if err != nil {
return cty.UnknownVal(cty.String), fmt.Errorf("error occured generating password %s", err.Error())
}
return cty.StringVal(string(out)), nil
},
})
// Md5Func constructs a function that computes the MD5 hash of a given string and encodes it with hexadecimal digits.
var Md5Func = makeStringHashFunction(md5.New, hex.EncodeToString)
// MakeFileMd5Func constructs a function that is like Md5Func but reads the
// contents of a file rather than hashing a given literal string.
func MakeFileMd5Func(baseDir string) function.Function {
return makeFileHashFunction(baseDir, md5.New, hex.EncodeToString)
}
// RsaDecryptFunc constructs a function that decrypts an RSA-encrypted ciphertext.
var RsaDecryptFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "ciphertext",
Type: cty.String,
},
{
Name: "privatekey",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
s := args[0].AsString()
key := args[1].AsString()
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "failed to decode input %q: cipher text must be base64-encoded", s)
}
rawKey, err := ssh.ParseRawPrivateKey([]byte(key))
if err != nil {
var errStr string
switch e := err.(type) {
case asn1.SyntaxError:
errStr = strings.ReplaceAll(e.Error(), "asn1: syntax error", "invalid ASN1 data in the given private key")
case asn1.StructuralError:
errStr = strings.ReplaceAll(e.Error(), "asn1: struture error", "invalid ASN1 data in the given private key")
default:
errStr = fmt.Sprintf("invalid private key: %s", e)
}
return cty.UnknownVal(cty.String), function.NewArgErrorf(1, errStr)
}
privateKey, ok := rawKey.(*rsa.PrivateKey)
if !ok {
return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "invalid private key type %t", rawKey)
}
out, err := rsa.DecryptPKCS1v15(nil, privateKey, b)
if err != nil {
return cty.UnknownVal(cty.String), fmt.Errorf("failed to decrypt: %s", err)
}
return cty.StringVal(string(out)), nil
},
})
// Sha1Func contructs a function that computes the SHA1 hash of a given string
// and encodes it with hexadecimal digits.
var Sha1Func = makeStringHashFunction(sha1.New, hex.EncodeToString)
// MakeFileSha1Func constructs a function that is like Sha1Func but reads the
// contents of a file rather than hashing a given literal string.
func MakeFileSha1Func(baseDir string) function.Function {
return makeFileHashFunction(baseDir, sha1.New, hex.EncodeToString)
}
// Sha256Func contructs a function that computes the SHA256 hash of a given string
// and encodes it with hexadecimal digits.
var Sha256Func = makeStringHashFunction(sha256.New, hex.EncodeToString)
// MakeFileSha256Func constructs a function that is like Sha256Func but reads the
// contents of a file rather than hashing a given literal string.
func MakeFileSha256Func(baseDir string) function.Function {
return makeFileHashFunction(baseDir, sha256.New, hex.EncodeToString)
}
// Sha512Func contructs a function that computes the SHA512 hash of a given string
// and encodes it with hexadecimal digits.
var Sha512Func = makeStringHashFunction(sha512.New, hex.EncodeToString)
// MakeFileSha512Func constructs a function that is like Sha512Func but reads the
// contents of a file rather than hashing a given literal string.
func MakeFileSha512Func(baseDir string) function.Function {
return makeFileHashFunction(baseDir, sha512.New, hex.EncodeToString)
}
func makeStringHashFunction(hf func() hash.Hash, enc func([]byte) string) function.Function {
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
s := args[0].AsString()
h := hf()
h.Write([]byte(s))
rv := enc(h.Sum(nil))
return cty.StringVal(rv), nil
},
})
}
func makeFileHashFunction(baseDir string, hf func() hash.Hash, enc func([]byte) string) function.Function {
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "path",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
path := args[0].AsString()
f, err := openFile(baseDir, path)
if err != nil {
return cty.UnknownVal(cty.String), err
}
h := hf()
_, err = io.Copy(h, f)
if err != nil {
return cty.UnknownVal(cty.String), err
}
rv := enc(h.Sum(nil))
return cty.StringVal(rv), nil
},
})
}
// UUID generates and returns a Type-4 UUID in the standard hexadecimal string
// format.
//
// This is not a pure function: it will generate a different result for each
// call. It must therefore be registered as an impure function in the function
// table in the "lang" package.
func UUID() (cty.Value, error) {
return UUIDFunc.Call(nil)
}
// UUIDV5 generates and returns a Type-5 UUID in the standard hexadecimal string
// format.
func UUIDV5(namespace cty.Value, name cty.Value) (cty.Value, error) {
return UUIDV5Func.Call([]cty.Value{namespace, name})
}
// Base64Sha256 computes the SHA256 hash of a given string and encodes it with
// Base64.
//
// The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied
// as defined in RFC 4634. The raw hash is then encoded with Base64 before returning.
// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
func Base64Sha256(str cty.Value) (cty.Value, error) {
return Base64Sha256Func.Call([]cty.Value{str})
}
// Base64Sha512 computes the SHA512 hash of a given string and encodes it with
// Base64.
//
// The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied
// as defined in RFC 4634. The raw hash is then encoded with Base64 before returning.
// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4
func Base64Sha512(str cty.Value) (cty.Value, error) {
return Base64Sha512Func.Call([]cty.Value{str})
}
// Bcrypt computes a hash of the given string using the Blowfish cipher,
// returning a string in the Modular Crypt Format
// usually expected in the shadow password file on many Unix systems.
func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) {
args := make([]cty.Value, len(cost)+1)
args[0] = str
copy(args[1:], cost)
return BcryptFunc.Call(args)
}
// Md5 computes the MD5 hash of a given string and encodes it with hexadecimal digits.
func Md5(str cty.Value) (cty.Value, error) {
return Md5Func.Call([]cty.Value{str})
}
// RsaDecrypt decrypts an RSA-encrypted ciphertext, returning the corresponding
// cleartext.
func RsaDecrypt(ciphertext, privatekey cty.Value) (cty.Value, error) {
return RsaDecryptFunc.Call([]cty.Value{ciphertext, privatekey})
}
// Sha1 computes the SHA1 hash of a given string and encodes it with hexadecimal digits.
func Sha1(str cty.Value) (cty.Value, error) {
return Sha1Func.Call([]cty.Value{str})
}
// Sha256 computes the SHA256 hash of a given string and encodes it with hexadecimal digits.
func Sha256(str cty.Value) (cty.Value, error) {
return Sha256Func.Call([]cty.Value{str})
}
// Sha512 computes the SHA512 hash of a given string and encodes it with hexadecimal digits.
func Sha512(str cty.Value) (cty.Value, error) {
return Sha512Func.Call([]cty.Value{str})
}