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