]>
Commit | Line | Data |
---|---|---|
1 | package funcs | |
2 | ||
3 | import ( | |
4 | "crypto/md5" | |
5 | "crypto/rsa" | |
6 | "crypto/sha1" | |
7 | "crypto/sha256" | |
8 | "crypto/sha512" | |
9 | "crypto/x509" | |
10 | "encoding/base64" | |
11 | "encoding/hex" | |
12 | "encoding/pem" | |
13 | "fmt" | |
14 | "hash" | |
15 | ||
16 | uuid "github.com/hashicorp/go-uuid" | |
17 | "github.com/zclconf/go-cty/cty" | |
18 | "github.com/zclconf/go-cty/cty/function" | |
19 | "github.com/zclconf/go-cty/cty/gocty" | |
20 | "golang.org/x/crypto/bcrypt" | |
21 | ) | |
22 | ||
23 | var UUIDFunc = function.New(&function.Spec{ | |
24 | Params: []function.Parameter{}, | |
25 | Type: function.StaticReturnType(cty.String), | |
26 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
27 | result, err := uuid.GenerateUUID() | |
28 | if err != nil { | |
29 | return cty.UnknownVal(cty.String), err | |
30 | } | |
31 | return cty.StringVal(result), nil | |
32 | }, | |
33 | }) | |
34 | ||
35 | // Base64Sha256Func constructs a function that computes the SHA256 hash of a given string | |
36 | // and encodes it with Base64. | |
37 | var Base64Sha256Func = makeStringHashFunction(sha256.New, base64.StdEncoding.EncodeToString) | |
38 | ||
39 | // MakeFileBase64Sha256Func constructs a function that is like Base64Sha256Func but reads the | |
40 | // contents of a file rather than hashing a given literal string. | |
41 | func MakeFileBase64Sha256Func(baseDir string) function.Function { | |
42 | return makeFileHashFunction(baseDir, sha256.New, base64.StdEncoding.EncodeToString) | |
43 | } | |
44 | ||
45 | // Base64Sha512Func constructs a function that computes the SHA256 hash of a given string | |
46 | // and encodes it with Base64. | |
47 | var Base64Sha512Func = makeStringHashFunction(sha512.New, base64.StdEncoding.EncodeToString) | |
48 | ||
49 | // MakeFileBase64Sha512Func constructs a function that is like Base64Sha512Func but reads the | |
50 | // contents of a file rather than hashing a given literal string. | |
51 | func MakeFileBase64Sha512Func(baseDir string) function.Function { | |
52 | return makeFileHashFunction(baseDir, sha512.New, base64.StdEncoding.EncodeToString) | |
53 | } | |
54 | ||
55 | // BcryptFunc constructs a function that computes a hash of the given string using the Blowfish cipher. | |
56 | var BcryptFunc = function.New(&function.Spec{ | |
57 | Params: []function.Parameter{ | |
58 | { | |
59 | Name: "str", | |
60 | Type: cty.String, | |
61 | }, | |
62 | }, | |
63 | VarParam: &function.Parameter{ | |
64 | Name: "cost", | |
65 | Type: cty.Number, | |
66 | }, | |
67 | Type: function.StaticReturnType(cty.String), | |
68 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
69 | defaultCost := 10 | |
70 | ||
71 | if len(args) > 1 { | |
72 | var val int | |
73 | if err := gocty.FromCtyValue(args[1], &val); err != nil { | |
74 | return cty.UnknownVal(cty.String), err | |
75 | } | |
76 | defaultCost = val | |
77 | } | |
78 | ||
79 | if len(args) > 2 { | |
80 | return cty.UnknownVal(cty.String), fmt.Errorf("bcrypt() takes no more than two arguments") | |
81 | } | |
82 | ||
83 | input := args[0].AsString() | |
84 | out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost) | |
85 | if err != nil { | |
86 | return cty.UnknownVal(cty.String), fmt.Errorf("error occured generating password %s", err.Error()) | |
87 | } | |
88 | ||
89 | return cty.StringVal(string(out)), nil | |
90 | }, | |
91 | }) | |
92 | ||
93 | // Md5Func constructs a function that computes the MD5 hash of a given string and encodes it with hexadecimal digits. | |
94 | var Md5Func = makeStringHashFunction(md5.New, hex.EncodeToString) | |
95 | ||
96 | // MakeFileMd5Func constructs a function that is like Md5Func but reads the | |
97 | // contents of a file rather than hashing a given literal string. | |
98 | func MakeFileMd5Func(baseDir string) function.Function { | |
99 | return makeFileHashFunction(baseDir, md5.New, hex.EncodeToString) | |
100 | } | |
101 | ||
102 | // RsaDecryptFunc constructs a function that decrypts an RSA-encrypted ciphertext. | |
103 | var RsaDecryptFunc = function.New(&function.Spec{ | |
104 | Params: []function.Parameter{ | |
105 | { | |
106 | Name: "ciphertext", | |
107 | Type: cty.String, | |
108 | }, | |
109 | { | |
110 | Name: "privatekey", | |
111 | Type: cty.String, | |
112 | }, | |
113 | }, | |
114 | Type: function.StaticReturnType(cty.String), | |
115 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
116 | s := args[0].AsString() | |
117 | key := args[1].AsString() | |
118 | ||
119 | b, err := base64.StdEncoding.DecodeString(s) | |
120 | if err != nil { | |
121 | return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode input %q: cipher text must be base64-encoded", s) | |
122 | } | |
123 | ||
124 | block, _ := pem.Decode([]byte(key)) | |
125 | if block == nil { | |
126 | return cty.UnknownVal(cty.String), fmt.Errorf("failed to parse key: no key found") | |
127 | } | |
128 | if block.Headers["Proc-Type"] == "4,ENCRYPTED" { | |
129 | return cty.UnknownVal(cty.String), fmt.Errorf( | |
130 | "failed to parse key: password protected keys are not supported. Please decrypt the key prior to use", | |
131 | ) | |
132 | } | |
133 | ||
134 | x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes) | |
135 | if err != nil { | |
136 | return cty.UnknownVal(cty.String), err | |
137 | } | |
138 | ||
139 | out, err := rsa.DecryptPKCS1v15(nil, x509Key, b) | |
140 | if err != nil { | |
141 | return cty.UnknownVal(cty.String), err | |
142 | } | |
143 | ||
144 | return cty.StringVal(string(out)), nil | |
145 | }, | |
146 | }) | |
147 | ||
148 | // Sha1Func contructs a function that computes the SHA1 hash of a given string | |
149 | // and encodes it with hexadecimal digits. | |
150 | var Sha1Func = makeStringHashFunction(sha1.New, hex.EncodeToString) | |
151 | ||
152 | // MakeFileSha1Func constructs a function that is like Sha1Func but reads the | |
153 | // contents of a file rather than hashing a given literal string. | |
154 | func MakeFileSha1Func(baseDir string) function.Function { | |
155 | return makeFileHashFunction(baseDir, sha1.New, hex.EncodeToString) | |
156 | } | |
157 | ||
158 | // Sha256Func contructs a function that computes the SHA256 hash of a given string | |
159 | // and encodes it with hexadecimal digits. | |
160 | var Sha256Func = makeStringHashFunction(sha256.New, hex.EncodeToString) | |
161 | ||
162 | // MakeFileSha256Func constructs a function that is like Sha256Func but reads the | |
163 | // contents of a file rather than hashing a given literal string. | |
164 | func MakeFileSha256Func(baseDir string) function.Function { | |
165 | return makeFileHashFunction(baseDir, sha256.New, hex.EncodeToString) | |
166 | } | |
167 | ||
168 | // Sha512Func contructs a function that computes the SHA512 hash of a given string | |
169 | // and encodes it with hexadecimal digits. | |
170 | var Sha512Func = makeStringHashFunction(sha512.New, hex.EncodeToString) | |
171 | ||
172 | // MakeFileSha512Func constructs a function that is like Sha512Func but reads the | |
173 | // contents of a file rather than hashing a given literal string. | |
174 | func MakeFileSha512Func(baseDir string) function.Function { | |
175 | return makeFileHashFunction(baseDir, sha512.New, hex.EncodeToString) | |
176 | } | |
177 | ||
178 | func makeStringHashFunction(hf func() hash.Hash, enc func([]byte) string) function.Function { | |
179 | return function.New(&function.Spec{ | |
180 | Params: []function.Parameter{ | |
181 | { | |
182 | Name: "str", | |
183 | Type: cty.String, | |
184 | }, | |
185 | }, | |
186 | Type: function.StaticReturnType(cty.String), | |
187 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
188 | s := args[0].AsString() | |
189 | h := hf() | |
190 | h.Write([]byte(s)) | |
191 | rv := enc(h.Sum(nil)) | |
192 | return cty.StringVal(rv), nil | |
193 | }, | |
194 | }) | |
195 | } | |
196 | ||
197 | func makeFileHashFunction(baseDir string, hf func() hash.Hash, enc func([]byte) string) function.Function { | |
198 | return function.New(&function.Spec{ | |
199 | Params: []function.Parameter{ | |
200 | { | |
201 | Name: "path", | |
202 | Type: cty.String, | |
203 | }, | |
204 | }, | |
205 | Type: function.StaticReturnType(cty.String), | |
206 | Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | |
207 | path := args[0].AsString() | |
208 | src, err := readFileBytes(baseDir, path) | |
209 | if err != nil { | |
210 | return cty.UnknownVal(cty.String), err | |
211 | } | |
212 | ||
213 | h := hf() | |
214 | h.Write(src) | |
215 | rv := enc(h.Sum(nil)) | |
216 | return cty.StringVal(rv), nil | |
217 | }, | |
218 | }) | |
219 | } | |
220 | ||
221 | // UUID generates and returns a Type-4 UUID in the standard hexadecimal string | |
222 | // format. | |
223 | // | |
224 | // This is not a pure function: it will generate a different result for each | |
225 | // call. It must therefore be registered as an impure function in the function | |
226 | // table in the "lang" package. | |
227 | func UUID() (cty.Value, error) { | |
228 | return UUIDFunc.Call(nil) | |
229 | } | |
230 | ||
231 | // Base64Sha256 computes the SHA256 hash of a given string and encodes it with | |
232 | // Base64. | |
233 | // | |
234 | // The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied | |
235 | // as defined in RFC 4634. The raw hash is then encoded with Base64 before returning. | |
236 | // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. | |
237 | func Base64Sha256(str cty.Value) (cty.Value, error) { | |
238 | return Base64Sha256Func.Call([]cty.Value{str}) | |
239 | } | |
240 | ||
241 | // Base64Sha512 computes the SHA512 hash of a given string and encodes it with | |
242 | // Base64. | |
243 | // | |
244 | // The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied | |
245 | // as defined in RFC 4634. The raw hash is then encoded with Base64 before returning. | |
246 | // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4 | |
247 | func Base64Sha512(str cty.Value) (cty.Value, error) { | |
248 | return Base64Sha512Func.Call([]cty.Value{str}) | |
249 | } | |
250 | ||
251 | // Bcrypt computes a hash of the given string using the Blowfish cipher, | |
252 | // returning a string in the Modular Crypt Format | |
253 | // usually expected in the shadow password file on many Unix systems. | |
254 | func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) { | |
255 | args := make([]cty.Value, len(cost)+1) | |
256 | args[0] = str | |
257 | copy(args[1:], cost) | |
258 | return BcryptFunc.Call(args) | |
259 | } | |
260 | ||
261 | // Md5 computes the MD5 hash of a given string and encodes it with hexadecimal digits. | |
262 | func Md5(str cty.Value) (cty.Value, error) { | |
263 | return Md5Func.Call([]cty.Value{str}) | |
264 | } | |
265 | ||
266 | // RsaDecrypt decrypts an RSA-encrypted ciphertext, returning the corresponding | |
267 | // cleartext. | |
268 | func RsaDecrypt(ciphertext, privatekey cty.Value) (cty.Value, error) { | |
269 | return RsaDecryptFunc.Call([]cty.Value{ciphertext, privatekey}) | |
270 | } | |
271 | ||
272 | // Sha1 computes the SHA1 hash of a given string and encodes it with hexadecimal digits. | |
273 | func Sha1(str cty.Value) (cty.Value, error) { | |
274 | return Sha1Func.Call([]cty.Value{str}) | |
275 | } | |
276 | ||
277 | // Sha256 computes the SHA256 hash of a given string and encodes it with hexadecimal digits. | |
278 | func Sha256(str cty.Value) (cty.Value, error) { | |
279 | return Sha256Func.Call([]cty.Value{str}) | |
280 | } | |
281 | ||
282 | // Sha512 computes the SHA512 hash of a given string and encodes it with hexadecimal digits. | |
283 | func Sha512(str cty.Value) (cty.Value, error) { | |
284 | return Sha512Func.Call([]cty.Value{str}) | |
285 | } |