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  }