aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/lang/funcs/crypto.go
blob: 5cb4bc5c146cc28ed1865fe6f211b5155f551418 (plain) (blame)
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
package funcs

import (
	"crypto/md5"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"hash"

	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"
)

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
	},
})

// 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), fmt.Errorf("failed to decode input %q: cipher text must be base64-encoded", s)
		}

		block, _ := pem.Decode([]byte(key))
		if block == nil {
			return cty.UnknownVal(cty.String), fmt.Errorf("failed to parse key: no key found")
		}
		if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
			return cty.UnknownVal(cty.String), fmt.Errorf(
				"failed to parse key: password protected keys are not supported. Please decrypt the key prior to use",
			)
		}

		x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return cty.UnknownVal(cty.String), err
		}

		out, err := rsa.DecryptPKCS1v15(nil, x509Key, b)
		if err != nil {
			return cty.UnknownVal(cty.String), 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()
			src, err := readFileBytes(baseDir, path)
			if err != nil {
				return cty.UnknownVal(cty.String), err
			}

			h := hf()
			h.Write(src)
			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)
}

// 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})
}